委托代理开发
实现思路如下:
- 在任务列表操作下添加一个设置委托代理的菜单,用户通过该菜单进入委托设置页的表单
- 开发一个委托设置页,并部署到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}&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