KimhillZhang  

委托代理开发

实现思路如下:

 

  • 在任务列表操作下添加一个设置委托代理的菜单,用户通过该菜单进入委托设置页的表单
  • 开发一个委托设置页,并部署到layouts目录下,该页面用户委托的设置、修改及删除
  • 创建一个列表库,存储用户的委托信息
  • 给任务列表添加一个字段,叫代理者,如果当前任务的分配者有委托设置,则该字段为受委托者的帐号信息,否则为空
  • 创建一个EventHandler并关联到任务列表,当有任务过来时,自动检查对于分配对象是否有委托设置,如果有,则将被委托人填到代理者一栏,如果没有则跳过。

WSPBuilder介绍

WSPBuilder的特点是根据文件夹结构来生成WSP文件,这是我个人觉得WSPBuilder最好的特性,也是最有用的功能。 现在,CodePlex上WSPBuilder提供了Visual Studio Addin的安装项目,该Extensions也可以在Visual Studio 2008上使用,该Extensions使得WSPBuilder更好的融合到Visual Studio的开发操作中,安装完"WSPBuilder Extensions 1.03 - Visual Studio Addin (0.9.8.0830)"后,你打开Visual Studio可以有如下WSPBuilder项目:

在创建好的WSPBuilder项目后,右键点击项目可以看到以下WSPBuilder菜单:

以上菜单中,值得注意的是以下两个:

  • Build WSP: 创建WSP文件,仅仅是创建而已,不会帮你部署!有很多时候我们其实只需要WSP文件(使用VSeWSS的人应该很希望VSeWSS也能这样)
  • Attach to IIS Worker Processes: 不用在点好几步,然后查找w3wp.exe进程,然后附加了,只要点一下即可开始调试!

开发的各种SharePoint部件(事件处理、Web Part、模板等)怎么方便的组合起来集成部署(知道用WSP,但是总是没有找到比较好的实施的方法);

做Solution Package的最主要的问题在于:

 

  • 如何正确的使用Solution Schema里的Element来部署不同的开发产出;
  • 编写正确的manifest.xml文件,打包成WSP文件

WSPBuilder的好处就在于,你需要做的就是把开发出来的东西放对文件夹!其他一切交给WSPBuilder,使用WSPBuilder自己的模板创建出来的项目,WSPBuilder会自动生成正相应的文件夹结构

所以,我的做法是使用WSPBuilder项目加上Visual Studio的Post-Event来满足要求:

 

  • 专门创建一个WSPBuilder Project,作为维护创建WSP文件的文件夹结构的项目(也就是说专门用来生成WSP文件的项目);
  • 为不同的功能模块(某个Web Part、事件处理程序等)创建不同的项目,这样方便进行代码管理,工作分配,多人协作,创建的项目加入到第一步创建的解决方案中;
  • 然后接着就是在WSPBuilder Project项目中,创建文件夹,直到每个功能模块对应的项目都能在该项目中找到相应的文件夹;对于每个功能模块对应的项目则在Post-Built中 加入命令行脚本,使在编译通过以后,把编译的结果拷贝到第一步创建的项目的文件夹目录下。

举个例子,你创建一个WSPBuilder的项目作为维护生成WSP文件的项目:

创建一个Web Part项目(用的是VSeWSS的Web Part模板),加入到解决方案中

在WSPBuilder项目中,添加一个叫DemoWebPartFeature的Blank Feature:

解决方案变成:

我会把自动生成的elements.xml文件删除,然后修改feature.xml文件:

把ElementManifests节改成以下样子:

然后在WSPBuilder项目中创建出80\bin目录,这时候解决方案样子如下:

然后,对DemoWebPart项目做一些设置,首先把WebPart1.webpart和WebPart1.xml文件的属性改成"Copy Always",这样在该项目的Debug目录中会出现这两个文件:

接下来,就是在Post-built中添加Copy命令行,把生成的文件拷贝到WSPBuilder项目对应的文件夹下:

 

copy "$(TargetDir)DemoWebPart.dll" "$(SolutionDir)WSPBuilder\80\bin\";

copy "$(TargetDir)WebPart1\WebPart1.xml" "$(SolutionDir)WSPBuilder\12\Template\Features\DemoWebPartFeature";

copy "$(TargetDir)WebPart1\WebPart1.webpart" "$(SolutionDir)WSPBuilder\12\Template\Features\DemoWebPartFeature";

 

这样,整个项目的初始创建和设置就完成了,把项目迁入到代码管理服务器中,以后就不需要再改动这些设置了。

 

当你编译DemoWebPart项目的时候,WSPBuilder项目的文件夹下就会是如下样子:

在WSPBuilder项目的文件夹里面就已经有了所有DemoWebPart相关的文件了,然后我们可以在WSPBuilder项目中创建WSP文件(Deploy和Upgrade菜单在第一次点击Build WSP后才会变成可用)

所有在WSPBuilder解决方案中的项目右键点击都会出现以上菜单,可以方便的点击"Attach to IIS Worker Processes"来调试。

 

这样的话

 

> 开发人员的工作过程就是:从代码管理服务器上获取解决方案的最新版本,然后迁出自己开发的项目,直接选择Deploy Solution,WSP会生成并自动部署到他的环境中,然后在代码中设置断点,点击"Attach to IIS Worker Processes"即可进行调试。

 

> 集成人员的工作过程:从代码管理服务器上获取最新的项目版本,然后Build WSP,把WSP部署到开发集成服务器上。这个过程,大家可以想象,使用自动build部署的方案完全是可以完成的。

 

委托数据列表库

创建一个保存委托数据的列表库,有两种方法可以创建,一种方法是我们直接到网站下面添加一个列表库,然后设置列表库的字段,但该方法显示在迁移时还要手工去完这些额外的事,对于部署上就比较麻烦了,第二种是将创建的操作用Feature完成,并一起封装到部署方案中,部署时只要运行部署方案就可以了。这里我们采用第二种方案实现该功能。

 

用Feature创建一个列表库一共有以下几步:

 

创建一个网站列集合,并将这些网站列添加到网站集中

创建一种内容类型,内容类型中包含了对先前创建的列的引用

创建ListTemplate,该模板中包含了对内容类型中的引用

通过ListTemplate创建ListInstance。

由于网站列的Feature范围均属于网站集范围,无法在网站范围内创建,因此这里要创建两个Feature来实现以上的功能。

 

MySiteContentTypes:实现将网站列、及自定义内容类型添加到网站集中

 

WorkFlowDelegate:实现创建ListTemplate、ListInstance等

 

创建MySiteContentTypes

在WSPBuilder的解决方案,中点击Add NewItem,选择WSPBuilder下的Blank Feature

创建完成后,添加两个xml文件,Fields.xml和ContentTypes.xml

其中必需注意的是ContentType 的ID属性。对于ContentType而言,是有继承关系的,它的继承关系不是通过属性等指定的,而是通过ID进行指定的,其实现方式是parent ContentType ID+self ID,在SDK中明确规定了两种命名方式:

 

Parent content type ID + two hexadecimal values (the two hexadecimal values cannot be "00"),父ID+两个16进制的数字,并且不可以为00

Parent content type ID + "00" + hexadecimal GUID,父ID+一个Guid。

这里采用了第二种方式,内容类型的ID为:0x00B74D936CCD9648AA8DA7E81DE6819675,表示直接生成了一种新的内容类型。Sharepoint中已有各种内容类型的Id如下:

在ContentType中还一个属性为Group属性,该属性指明在本内容类型添加到网站中后,归到哪个分组下,可以在网站设置=>库下面的内容类型中看到创建成功的内容类型及其所在的组,如下:

创建WorkFlowDelegate

该Feature的范围是网站,为了减少Feature数量,本Feature除了实现创建委托数据列表外,还将实现其实的一些功能。在解决方案中选择添加新项,内容是WSPBuilder下的Feature with Receiver,即带后台代码的feature,在feature被激活或者停止时会触发相应的后台代码进行操作。

创建完成后,工具除了创建了一个Feature文件夹外,还创建了一个FeatureCode文件夹,下面生成了WorkFlowDelegate.cs代码文件,在feature被激活或者停止会触发该类下的相应方法。

Feature.xml

由于本Feature创建的列表库中的列存在选择的Feature中,因此本Feature依赖于先前的Feature,在本feature的定义文件中添加如下代码,表示这种依赖有关系

 

<ActivationDependency FeatureId="{641aec55-a881-4698-ab17-1d55f599ee72}"/>

 

创建ListTemplate一般需要两个文件,一个定义文件,一个是schema文件,按上图的文件结构创建这两个文件DelegateList.xml和schema.xml文件,创建完成后,在Feature中添加对DelegateList.xml文件的引用。Feature文件最终为:

 

Schemal.xml

Schemal.xml文件可以到12目录下的tempate\feature中找一个拷贝过来,如12\TEMPLATE\FEATURES\CustomList\CustList这个目录,拷贝完后,修改一下ContentTypes和Fields节的内容:修改如下:

WorkflowDelegateHandler.cs

<ContentTypes>

<ContentTypeRef ID="0x00B74D936CCD9648AA8DA7E81DE6819675"></ContentTypeRef>

</ContentTypes>

<Fields>

<Field ID="{6323bea9-6aff-40c6-85b8-cbdd13855f22}" Type="User" Name="DelegateUser" DisplayName="用户" List="UserInfo" Required="TRUE" ShowField="ImnName" UserSelectionScope="0" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="DelegateUser" ColName="int1" />

<Field Name="ProxyUser" Type="User" DisplayName="代理人" List="UserInfo" Required="TRUE" ShowField="ImnName" UserSelectionScope="0" ID="{60499066-90c3-4785-a099-aa04d4840108}" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="ProxyUser" ColName="int2" />

<Field Name="BeginTime" Type="DateTime" DisplayName="开始时间" Required="TRUE" Format="DateTime" IMEMode="inactive" ID="{b93bb970-9a36-40b1-8a39-8bd741813292}" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="BeginTime" ColName="datetime1" CalType="0" >

<Default>[today]</Default>

</Field>

<Field Name="EndTime" Type="DateTime" DisplayName="结束时间" Required="TRUE" Format="DateTime" IMEMode="inactive" ID="{d8153a32-5255-4e9e-8c67-fab307835c47}" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="EndTime" ColName="datetime2" CalType="0" >

<Default />

</Field>

<Field Name="WorkFlowType" Type="Text" DisplayName="流程类型" Required="FALSE" MaxLength="255" ID="{93ae19fe-f213-4214-b227-392648a7b23d}" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="WorkFlowType" ColName="nvarchar3" IMEMode="">

<Default />

</Field>

</Fields>

修改完成后效果如下:

Elements.xml

到此列表库的模板已经创建成功,现在可以通过此模板创建实例了,在elements.xml文件中添加:

<ListInstance Id="0"

Description="用户委托代理数据"

OnQuickLaunch="False"

TemplateType="5100"

Title="委托数据"

Url="Lists/DelegateData" />

 

其中TemplateType值与DelegateList.xml文件中ListTemplate的Type属性值要一致。

 

WorkFlowDelegate.cs

在WorkFlowDelegate.cs页面中创建Feature激活时的操作,这里的操作有两个,一是给工作流任务列表添加一个字段,叫代理人,另一个工作是修改任务列表的我的任务视图,原视图的过滤条件是分配对象=本人,改为分配对象=本人 或者 代理者=本人

 

具体代码如下 :

public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

using (SPWeb web = properties.Feature.Parent as SPWeb)

{

if (web == null)

{

return;

}

int listNum=web.Lists.Count;

for(int i=0;i<listNum;i++)

{

SPList list = web.Lists[i];

if (list.BaseTemplate != SPListTemplateType.Tasks)

{

continue;

}

if (list.Fields.ContainsField(this.m_proxyUserFieldName))

{

continue;

}

//添加字段

string fieldxml = string.Format("<Field Type=\"User\" Name=\"{0}\" DisplayName=\"{0}\" List=\"UserInfo\" UserSelectionScope=\"0\" />",

this.m_proxyUserFieldName);

list.Fields.AddFieldAsXml(fieldxml);

SPField field = list.Fields[this.m_proxyUserFieldName];

if (field == null)

{

continue;

}

field.Title = "代理者";

field.Update();

SPView myTaskView = list.Views["我的任务"];

if (myTaskView == null)

{

Continue;

}

//更新我的任务视图

string query = string.Format(@"<OrderBy><FieldRef Name=""Status"" /></OrderBy><Where><Or><Eq><FieldRef Name=""AssignedTo"" /><Value Type=""Integer""><UserID Type=""Integer"" /></Value></Eq><Eq><FieldRef Name=""{0}"" /><Value Type=""Integer""><UserID Type=""Integer"" /></Value></Eq></Or></Where>"

, this.m_proxyUserFieldName);

myTaskView.Query = query;

myTaskView.Update();

}

}

}

创建委托菜单及处理页面

添加委托菜单

即在工作流任务列表的操作菜单下添加一个设置委托的菜单,完成后效果如下 :

该步骤比较简单,可以将该功能归入WorkFlowDelegate这个Feature中,在其elements.xml加入以下代码即可:

<CustomAction Id="DelegateAction.DelegateItem"

RegistrationType="List"

RegistrationId="107"

GroupId="ActionsMenu"

Location="Microsoft.SharePoint.StandardMenu"

Sequence="1000"

ImageUrl="/_layouts/images/titlegraphic.gif" Description="设置任务委托人"

Title="设置委托">

<UrlAction Url="javascript:window.location= '{SiteUrl}/_layouts/SetDelegate.aspx?ListId={ListId}&amp;Source='+window.location;"/>

</CustomAction>

 

其中:

RegistrationId为107表示在任务列表中添加菜单,其它类型的列表库类型可以通过SDK进行查找

UrlAction的URL中可以有一些占位符,在SDK中介绍:

~site - Web site (SPWeb) relative link.

~sitecollection - site collection (SPSite) relative link.

In addition, you can use the following tokens within a URL:

{ItemId} - Integer ID that represents the item within a list.

{ListId} - GUID that represents the list.

{SiteUrl} - URL of the Web site (SPWeb).

{RecurrenceId} - Recurrence index. This token is not supported for use in the context menus of list items.

 

另外还需注意一点是占位符只能被替换一次,比如说如果URL中你要使用到{SiteUrl}多次的话,那只能通过其它方式进行写了,由于URL中不仅支持链接地址,还支持javascript脚本,因此,出现了一种取巧的方法,在http://weblogs.asp.net/jan/archive/2007/09/05/using-the-current-page-url-in-the-urlaction-of-a-sharepoint-feature.asp 中有描述如何在URL中使用脚本。

委托处理页

在CustomAction中我们给出一该菜单的处理地址为_layouts/SetDelegate.aspx,因此下面我们就在Layout目录下创建一个这样的文件:

只要在项目中对Microsoft.SharePoint.dll进行了引用,那们就可以直接在SetDelegate.aspx页面中,我们可以直接使用sharepiont的一些门资源了,如:sharepiont的母版页,日期控件、选人控件等。

SetDelegate.aspx页面代码如下:

<asp:Content ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">

设置委托人

</asp:Content>

<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">

<table cellpadding="5" cellspacing="1" style="background-color:Fuchsia" width="100%">

<tr style="background-color:White">

<td>

开始时间

</td>

<td>

<SharePoint:DateTimeControl HoursMode24="true" ID="txtDateBegin" runat="server"></SharePoint:DateTimeControl>

</td>

</tr>

<tr style="background-color:White">

<td>

结束时间

</td>

<td>

<SharePoint:DateTimeControl HoursMode24="true" ID="txtDateEnd" runat="server"></SharePoint:DateTimeControl>

</td>

</tr>

<tr style="background-color:White">

<td>

代理者

</td>

<td>

<SharePoint:PeopleEditor ID="ProxyUser" runat="server" MultiSelect="false" ShowCreateButtonInActiveDirectoryAccountCreationMode="true" SelectionSet="User" AllowEmpty="false"/>

</td>

</tr>

<tr style="background-color:White">

<td colspan="2">

<asp:Label ID="lbErr" runat="server" Text="错误信息" Visible="false" ForeColor="Red"></asp:Label>

<asp:Button ID="btSubmit" runat="server" Text="设置" />

<asp:Button ID="btDelete" runat="server" Text="删除" Visible="false" />

</td>

</tr>

</table>

</asp:Content>

由于页面不支持可视化编辑,并且不有.cs.designer页面,因此必须要在后台页面的OnInit方法中生成.aspx页面中两个控件的点击事件,代码如下:

 

protected override void OnInit(EventArgs e)

{

this.btSubmit.Click += new EventHandler(btSubmit_Click);

this.btDelete.Click += new EventHandler(btDelete_Click);

}

 

其它的和以前的页面处理一样,具体的业务逻辑代码就不公布出来了,大家自己写了。业务逻辑大概就是:

PageLoad方法:

如果用户没有设置代理,则删除操作不可用

如果有设置代理,删除操作可用,并将已设置的代理人及时间信息读取出来,填到表单上

btSubmit_Click:

将代理保存到先前创建的委托数据列表库中,并且更新本任务列表库(URL中已经传入了任务列表的ID)中分配给这个的待办任务,只处理未完成的任务,已完成的任务不处理

btDelete_Click:

删除用户的委托代理数据,并更新任务列表中当前用户的委托代理信息

 

创建EventHandler

对于任务列表中的每一条待办任务创建成功后,都要检验一下对于分配对象是否有设置委托代理,如果没有设置的话,则不做处理,如果有设置的话,则修改该待办任务,填写代理者字段。

在FeatureCode目录下创建一个WorkflowDelegateHandler.cs文件来处理该EventHandler,文件代码如下:

class WorkflowDelegateHandler : SPItemEventReceiver

{

public override void ItemAdded(SPItemEventProperties properties)

{

base.ItemAdded(properties);

this.DisableEventFiring();

SPListItem item = properties.ListItem;

using (SPWeb web = properties.OpenWeb())

{

SPContext context=SPContext.GetContext(web);

int taskOwnerId = Utility.FomatUserID(item["AssignedTo"]);

SPUser user = web.SiteUsers.GetByID(taskOwnerId);

WorkflowDelegateBLL bll = new WorkflowDelegateBLL(user);

bll.Context = context;

bll.SetTaskDelegate(item);

}

this.EnableEventFiring();

}

}

 

Elements.xml

编写完成后,最后一步就是将该EventHandler关联到任务列表,该操作也可以用Feature来完成,打开WorkFlowDelegate的Elements.xml文件,加入:

 

<Receivers ListTemplateId="107">

<Receiver>

<Name>DelegateAddingEventHandler</Name>

<Type>ItemAdded</Type>

<SequenceNumber>10000</SequenceNumber>

<Assembly>MossTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d71750f77f308af</Assembly>

<Class> MossTest.WorkflowDelegateHandler</Class>

<Data></Data>

<Filter></Filter>

</Receiver>

</Receivers>

 

ListTemplateId值为107,表示该操作仅对任务列表类型的列表库进行操作。其它类型列表库可以查看SDK相关文档。

 

本文来自CSDN博客,转载请标明出处:file:///F:/SharePoint2007/Designer工作流网页/Infopath+Designer工作流设计之四--委托代理开发%20-%20北极星博客%20-%20CSDN博客.htm

posted on 2009-07-23 15:16  KimhillZhang  阅读(1014)  评论(0编辑  收藏  举报