KimhillZhang  

1  SharePoint Designer工作流的迁移

SharePoint Designer设计工作流的功能是提供给最终用户使用的,就如同用SPD定制站点页面一样,并没有提供专门的流程迁移工具。

最终用户可以直接在生产环境中设计流程。对开发人员来说,如果在开发环境中设计好了一个比较复杂的流程,需要迁移到生产环境,必须手工操作,可以参考如下步骤。

Step1  确保生产环境已经建好了相应的列表,并确保列表的配置跟开发环境完全一致,包括列表名称、列表的所有栏。

Step2  SharePoint Designer打开生产环境的站点,新建一个与开发环境名称一样的流程,绑定到同名称的列表,直接保存。

Step3  SharePoint Designer打开开发环境的站点,将工作流的所有文件复制到生产环境。

Step4  打开所有工作流的文件,将其中所有的列表项ID替换成生产环境相应的列表项ID

2 SharePoint Designer工作流的扩展

SharePoint Designer提供了常用的很多条件和活动,但是这些条件和活动并不能满足所有需要。本节就来讲述SPD动作和条件的扩展。

2.1  动作的扩展

SPD工作流的动作是一个普通的WF活动类。WF活动类开发好以后,部署到GAC中,然后修改SPD工作流的配置文件,注册新开发的活动。

SPD工作流配置文件路径为C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\2052\Workflow,2052表示中文语言,英文语言的配置文件路径为C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\ Workflow,在这个路径下,有个WSS.ACTIONS文件,这是一个XML文件,这个文件里面配置了所有系统自带的动作和条件。

WSS.ACTIONS在Actions节点下声明了所有的动作。SDP工作流是基于规则来定义的。在ACTIONS配置文件中,将动作类的属性与各种规则设计器关联。以下是"从用户处收集数据"动作的声明。

<Action Name="从用户处收集数据"

     ClassName="Microsoft.SharePoint.WorkflowActions.CollectDataTask"

     Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=

neutral, PublicKeyToken=71e9bce111e9429c"

     AppliesTo="all"

     CreatesTask="true"

     Category="任务操作">

     <RuleDesigner Sentence=" %2 处收集 %1 (输出到 %3)">

        <FieldBind Field="Title,ContentTypeId" DesignerType="Survey" Text=

"数据" Id="1"/>

        <FieldBind Field="AssignedTo" DesignerType="SinglePerson" Text="此用户" Id=

"2"/>

   <FieldBind Field="TaskId" DesignerType="ParameterNames" Text="collect" Id="3"/>

     </RuleDesigner>

     <Parameters>

        <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.

WorkflowContext, Microsoft.SharePoint.WorkflowActions"

Direction="In" />

        <Parameter Name="ContentTypeId" Type="System.String, mscorlib" Direction=

"In" />

        <Parameter Name="AssignedTo" Type="System.String, mscorlib" Direction=

"In" />

        <Parameter Name="Title" Type="System.String, mscorlib" Direction="In" />

        <Parameter Name="TaskId" Type="System.Int32, mscorlib" Direction="Out" />

     </Parameters>

    </Action>

以下是对配置文件中主要元素的说明。

l         ClassNameAssembly分别是动作类的全名和所在的程序集。

l         AppliesTo指定动作是应用到文档库还是列表,允许的值为listall

l         CreatesTask指定是否需要创建任务项。

l         Parameters节点中声明了WF活动类需要跟上下文数据进行绑定或进行设置的属性。

在Parameter节点中,Name为WF活动类的属性名,Type为属性的类型,Direction声明属性是需要赋值的还是对外提供值,有3个可选值:In、Out、Optional,指定In时必须给属性赋值,指定Out时必须将属性跟某个工作流变量进行绑定,Optional表示可选项,指定Optional时可以不对属性进行操作。以下为3个特殊的Parameter。

l         __Context:当Action类需要访问工作流数据时,必须声明此属性,类型是Microsoft.SharePoint.WorkflowActions.WorkflowContext

l         __ListId:当需要访问工作流关联的列表时,需要指定此属性。

l         __ItemID:当需要访问工作流关联的列表项时,需要指定此属性。

以上的3个属性定义在Action类中,然后声明在配置文件中,工作流运行时引擎会自动为其赋值。

RuleDesigner节点声明了对Action类属性的设计规则。Sentence指定了设计时显示的提示信息,其中的占位符号%与RuleDesigner中的FieldBind的ID属性配置,FieldBind定义了对某个属性的设计规则。Field为属性的名字,Text为%占位符处显示的文本,ID跟占位符%前的数字对应。DesignerType指定了对属性应用的设计器,系统支持如下的设计器。

l         Survey:生成收集数据的表单,表单对应的内容类型ID和标题需要分别映射到两个属性中。

l         SinglePerson:单个用户或用户组。

l         Person:多个用户或用户组。

l         ParameterNames:工作流变量。

l         StringBuilder:生成可以跟当前工作流数据混合的字符串。

l         Operator:选项,用Option指定可选值。如:

<FieldBind Field="TaskMode" DesignerType="Operator" Text="此模式" Id="3">

          <Option Name="RequireOne" Value="RequireOne"/>

          <Option Name="RequireAll" Value="RequireAll"/>

  </FieldBind>

l         fieldNames:列表栏,采用此设计器将工作流关联列表的某个栏和Action类的属性绑定。

l         E-mail:邮件设计器,设计邮件的标题、内容和收件人等,需要将收件人、抄送人、标题、内容4个属性分别绑定,如:

<RuleDesigner Sentence="电子邮件 %1">

        <FieldBind Field="To,CC,Subject,Body" Text="此电子邮件" DesignerType=

"Email" Id="1"/>

      </RuleDesigner>

      <Parameters>      

        <Parameter Name="Body" Type="System.String, mscorlib" Direction="Optional" />

        <Parameter Name="To" Type="System.Collections.ArrayList,

mscorlib" Direction="In" />

        <Parameter Name="CC" Type="System.Collections.ArrayList, mscorlib"

Direction="Optional" />

        <Parameter Name="Subject" Type="System.String, mscorlib" Direction="In" />

      </Parameters>

2.2  动作扩展示例——自定义E-mail活动

系统默认的发送邮件动作只能发送简单的文本,很多时候,用户希望审批任务的提醒邮件中带一个链接,可以直接链接到任务操作页面。本节我开发一个可以发送任务链接的E-mail活动,这个E-mail活动具有系统发送邮件动作的所有功能,可以动态绑定收件人、指定主题等,如图1所示。唯一跟系统默认功能的区别就是:在发送出去的邮件内容中带有任务操作链接。

 

Step1  新建一个工作流活动项目,如图2所示。将默认产生的Activity1类改名为"MailWithTaskLinkActivity"。

新建工作流活动项目

Step2  添加Microsoft.SharePoint.dllmicrosoft.sharepoint.WorkflowActions.dll两个程序集的引用。这两个程序集位于服务器上的C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI目录中。

Step3  切换到代码视图,将MailWithTaskLinkActivity的基类由SequenceActivity改为Activity

复核活动可以从SequenceActivity类继承,但是MailWithTaskLinkActivity并不需要使用子活动(也就是不是一个复核活动),所有应该从基本的Activity类继承。

Step4  添加对SharePoint API主要名称空间的引用,代码如下所示。

using Microsoft.SharePoint;

using Microsoft.SharePoint.Utilities;

using Microsoft.SharePoint.Workflow;

using Microsoft.SharePoint.WorkflowActions;

namespace CodeArt.SharePoint.WorkflowActions

{

    public class MailWithTaskLinkActivity : Activity

    {

        public MailWithTaskLinkActivity()

        {           

        }

    }

}

Step5  希望MailWithTaskLinkActivity部署成SharePoint Designer的动作之后,依然具有系统自带的发送邮件活动的功能可以动态绑定到收件人、指定邮件主题等,所以需要给MailWithTaskLinkActivity添加相应的属性。添加一个__Context属性,代码如下所示。

public static DependencyProperty __ContextProperty =

          DependencyProperty.Register("__Context", typeof(WorkflowContext),

typeof(SendMail));

        [Browsable(true), ValidationOption(ValidationOption.Required),

DesignerSerializationVisibility(DesignerSerializationVisibility.

Visible), Description("Context")]

        public WorkflowContext __Context

        {

            get

            {

                return (WorkflowContext)base.GetValue(__ContextProperty);

            }

            set

            {

                base.SetValue(__ContextProperty, value);

            }

        }

__Context属性用来绑定到工作流的上下文,以便获取工作流运行环境的网站集、网站等信息。这个属性的名称是不能变的,后面的步骤会讲述如何在配置文件中配置这个属性。添加一个__ListId属性,代码如下所示。

public static DependencyProperty __ListIdProperty = DependencyProperty.Register(

"__ListId", typeof(string), typeof(MailWithTaskLinkActivity));

        [ValidationOption(ValidationOption.Required)]

        public string __ListId

        {

            get

            {

                return (string)base.GetValue(__ListIdProperty);

            }

            set

            {

                base.SetValue(__ListIdProperty, value);

            }

        }

__ListId属性用来绑定到工作流的关联的列表ID。添加一个__ListItem属性,代码如下所示。

public static DependencyProperty __ListItemProperty = DependencyProperty.Register(

"__ListItem", typeof(int), typeof(MailWithTaskLinkActivity));

        [ValidationOption(ValidationOption.Required)]

        public int __ListItem

        {

            get

            {

                return (int)base.GetValue(__ListItemProperty);

            }

            set

            {

                base.SetValue(__ListItemProperty, value);

            }

        }

__ListItem属性用来绑定到工作流的关联的列表项ID。添加一个To属性,代码如下所示。

public static DependencyProperty ToProperty = DependencyProperty.Register("To",

typeof(ArrayList), typeof(MailWithTaskLinkActivity));

        [ValidationOption(ValidationOption.Required)]

        public ArrayList  To

        {

            get

            {

                return (ArrayList)base.GetValue(ToProperty);

            }

            set

            {

                base.SetValue(ToProperty, value);

            }

        }

To属性是一个ArrayList类型的集合,在SharePoint Designer进行设计的时候可以直接绑定到用户。同样,添加邮件的抄送(CC)、暗送(BCC)、主题(Subject)、内容(Body4个属性代码如下所示。

public static DependencyProperty CCProperty = DependencyProperty.Register("CC",

typeof(ArrayList), typeof(MailWithTaskLinkActivity));

        //抄送

        [ValidationOption(ValidationOption.Optional)]

        public ArrayList CC

        {

            get

            {

                return (ArrayList)base.GetValue(CCProperty);

            }

            set

            {

                base.SetValue(CCProperty, value);

            }

        }

        public static DependencyProperty BCCProperty = DependencyProperty.Register(

"BCC", typeof(ArrayList), typeof(MailWithTaskLinkActivity));

        //暗送

        [ValidationOption(ValidationOption.Optional)]

        public ArrayList BCC

        {

            get

            {

                return (ArrayList)base.GetValue(BCCProperty);

            }

            set

            {

                base.SetValue(BCCProperty, value);

            }

        }

        public static DependencyProperty SubjectProperty = DependencyProperty.

Register("Subject", typeof(String), typeof(MailWithTaskLinkActivity));

        //主题

        [ValidationOption(ValidationOption.Required)]

        public string Subject

        {

            get

            {

                return (string)base.GetValue(SubjectProperty);

            }

            set

            {

                base.SetValue(SubjectProperty, value);

            }

        }

        public static DependencyProperty BodyProperty = DependencyProperty.Register(

"Body", typeof(String), typeof(MailWithTaskLinkActivity));

        //邮件内容

        [ValidationOption(ValidationOption.Optional)]

        public string Body

        {

            get

            {

                return (string)base.GetValue(BodyProperty);

            }

            set

            {

                base.SetValue(BodyProperty, value);

            }

        }

Step6  上一步把MailWithTaskLinkActivity需要的所有属性都添加好了,下面来添加发送邮件的处理代码。重载Execute方法,代码如下所示。

        protected override ActivityExecutionStatus Execute(

ActivityExecutionContext provider)

        {

            //获取到工作流服务

            ISharePointService service = (ISharePointService)provider.GetService(

typeof(ISharePointService));

            if (service == null)

            {

                throw new InvalidOperationException();

            }

            try

            {

                //获取到列表

                SPList list = __Context.Web.Lists[new Guid(__ListId)];

                //获取到列表项

                SPListItem item = list.GetItemById(Convert.ToInt32(__ListItem));

                //计算任务查看URL

                string url = this.__Context.Web.Url +

                "_layouts/codeArt/SPTaskRedirect.aspx?ListId=" + item.ParentList.ID +

"&ItemId=" + item.ID;               

                //发送邮件参数

                StringDictionary headers = new StringDictionary();

                headers["to"] = this.ParseSendTo(this.To);

                headers["subject"] = this.Subject;

                if (null != this.CC)

                {

                    headers["cc"] = this.ParseSendTo(this.CC);

                }

                if (null != this.BCC)

                {

                    headers["bcc"] = this.ParseSendTo(this.BCC);

                }

                string body = null;

                if (null != this.Body)

                {

                    Activity parent = provider.Activity;

                    while (parent.Parent != null)

                    {

                        parent = parent.Parent;

                    }

                    //处理邮件内容中的属性绑定,Helper是系统自带的类

                    body = Helper.ProcessStringField(this.Body, parent, this.__Context);

                }

                body += "<br/><a href='" + url + "'><b>点击此处查看或处理任务</b></a>";

                //发送邮件

                service.SendEmail(base.WorkflowInstanceId, false, headers, body);

                return ActivityExecutionStatus.Closed;

            }

            catch (Exception ex)

            {

                //将异常信息记录到日志列表

                service.LogToHistoryList(base.WorkflowInstanceId,

SPWorkflowHistoryEventType.WorkflowError,

__Context.Web.CurrentUser.ID, TimeSpan.MinValue,

"MailWithTaskLinkActivity Error",

                    ex.Message + ex.StackTrace, "");

            }

            return ActivityExecutionStatus.Faulting;

        }

发送邮件活动一般放置在从用户处收集数据活动之前,那么在发送邮件活动执行的时候审批任务是没有创建的,这时候无法获取任务的路径或ID,所以我们采用一个中转页面(SPTaskRedirect.aspx),将列表项的ID传给这个页面,SPTaskRedirect.aspx负责转向任务操作页面。ParseSendTo方法用来将存放邮件地址和账号的ArraryList转换成合法的收件人格式,这个函数反编译自系统自带的MailActivity

Step7  编写SPTaskRedirect.aspx页面代码,并将其复制到12\TEMPLATE\LAYOUTS\CodeArt目录下。

<%@ Page Language="C#" %>

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c" %>

<%@ Import Namespace="Microsoft.SharePoint" %>

<script runat="server">   

void Page_Load(object sender , EventArgs e)

{

    //获取到列表

    SPList list = SPContext.Current.Web.Lists[ new Guid(Request.QueryString

["ListId"]) ];

    //获取到列表项

    SPListItem item = list.GetItemById(Request.QueryString["ItemID"])

    //当前用户ID

    int useId = SPContext.Current.Web.CurrentUser.ID;  

    Microsoft.SharePoint.Workflow.SPWorkflowTask currentTask = null;

    //查找当前用户的任务

    foreach (Microsoft.SharePoint.Workflow.SPWorkflowTask task in item.Tasks)

    {

        if ("" + task["PercentComplete"] == "1")

            continue;

        string assignedTo = "" + task["AssignedTo"];

        if (assignedTo == "")

            continue;

        SPFieldUserValue user = new SPFieldUserValue(SPContext.Current.Web,

assignedTo);

        if (useId == user.LookupId)

        {

            currentTask = task;

            break;

        }

    }

    if (currentTask == null)

    {

        Response.Write("任务不存在或已被删除。");

        return;

    }

    //转向任务编辑页面

    Response.Redirect(currentTask.ContentType.EditFormUrl + "?List=" +

currentTask.ParentList.ID + "&ID=" + currentTask.ID);

}

</script>

Step8  将项目进行签名,利用reflector找到程序集的全名,代码如下所示。

CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d

Step9  创建一个XML文件,改名为CodeArt.Actions,并将其复制到12\TEMPLATE\2052\ Workflow目录下。

<?xml version="1.0" encoding="utf-8" ?>

<WorkflowInfo>

<Actions Sequential="then" Parallel="and">

<Action Name="CodeArt_发送电子邮件"

    ClassName="CodeArt.SharePoint.WorkflowActions.MailWithTaskLinkActivity"

      Assembly="CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0, Culture=

neutral, PublicKeyToken=8d0e2047bbdccb4d"

    Category="SmartForm"

   AppliesTo="all"

     UsesCurrentItem="true"

     >

   <RuleDesigner Sentence="电子邮件 %1">

        <FieldBind Field="To,CC,Subject,Body" Text="此电子邮件" DesignerType=

"Email" Id="1"/>

   </RuleDesigner>

   <Parameters>

        <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.

WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction=

"In"/>

        <Parameter Name="__ListId" Type="System.String, mscorlib" Direction=

"In" />

        <Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction=

"In" />

        <Parameter Name="Body" Type="System.String, mscorlib" Direction=

"Optional" />

        <Parameter Name="To" Type="System.Collections.ArrayList, mscorlib"

Direction="In" />

        <Parameter Name="CC" Type="System.Collections.ArrayList, mscorlib"

Direction="Optional" />

        <Parameter Name="Subject" Type="System.String, mscorlib" Direction=

"In" />

   </Parameters>

</Action>

</Actions>

</WorkflowInfo>

RuleDesigner节点配置了采用E-mail设计器设计MailWithTaskLinkActivity 类的ToCCSubjectBody 4个属性。在Parameters节点中,指定了相应属性的输入/输出类型。

Step10 将程序集(CodeArt.SharePoint.WorkflowActions.dll)部署到GAC中,配置文件(CodeArt.Actions)添加到12\TEMPLATE\2052\Workflow目录中后,还需要修改应用程序的web.config文件,在authorizedType节点下添加如下配置:

<authorizedType Assembly="CodeArt.SharePoint.WorkflowActions,Version=1.0.0.0,

Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d" Namespace=

"CodeArt.SharePoint.WorkflowActions" TypeName="*" Authorized="True" />

配置完成后,在SharePoint Designer中设计流程的时候,就可以选择这个自定义的发送邮件动作了,如图3所示。

3 选择自己开发的发送邮件动作

2.3  条件的扩展

SPD的条件是一个普通的类静态函数,系统默认的条件同样配置在WSS.ACTIONS文件中。以下是"标题域包含关键字"条件的配置。

   <Condition Name="标题域包含关键字"

                FunctionName="WordsInTitle"

                ClassName="Microsoft.SharePoint.WorkflowActions.Helper"

                Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c"

                AppliesTo ="list"

                UsesCurrentItem="true">

     <RuleDesigner Sentence="标题域包含 %1">

        <FieldBind Id="1" Field="_1_" Text="关键字"/>

     </RuleDesigner>

     <Parameters>

        <Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />

     </Parameters>

   </Condition>

与动作类的配置很类似,ClassNameAssembly分别是条件方法所在的类的全名和类所在的程序集,FunctionName是静态函数的函数名。实现条件的静态函数必须采用如下签名。

public static bool SomeFunction(WorkflowContext context, string listId,

int listItem, string var1,string var2)

{

 //…

}

其中contextlistIdlistItem分别是工作流上下文数据、当前列表的ID和当前列表项的ID,这3个参数由工作流引擎来赋值。第4个以后的参数是额外的特殊参数,需要声明到配置文件的Parameters节点中。

Parameter节点的Name属性表示参数的顺序号,因为前3个参数是系统必需的,所以从第4个参数开始,顺序号为1,并且要用_1_的形式。

RuleDesigner中的FieldBind定义了参数的设计器,通过Field跟Parameter中声明的参数对应。FieldBind支持的其他属性(如DesignerType)和动作的配置完全一样。

2.4  条件扩展示例——自定义E-mail活动

采用SharePoint Designer设计审批流程的时候,很多情况下需要按照提交人的将审批任务分配给不同的审批人,比如,普通员工的请假需要部门经理审批,部门经理的请假需要公司总监来审批。本节我们就来开发这样一个条件,可以用于判断流程的启动用户是否属于某个网站组。

Step1  创建条件方法。新建一个类WorkflowConditions,添加一个静态函数CompareOriginatorGroup,完整的代码如下所示。

namespace CodeArt.SharePoint.WorkflowActions

{

    ///<summary>

    ///条件类

    ///</summary>

    public class WorkflowConditions

    {

        ///<summary>

        ///比较流程发起人所属组

        ///</summary>

        ///<param name="context">工作流上下文</param>

        ///<param name="listId">关联列表ID</param>

        ///<param name="itemId">关联列表项ID</param>

        ///<param name="groupName">组名</param>

        ///<returns></returns>

    public static bool CompareOriginatorGroup(WorkflowContext context, string listId,

      int itemId,

            string groupName )

        {

            //SPList list = context.Web.Lists[new Guid(listId)]; //获取关联的列表

            //获取关联的列表项

            //SPListItem item = list.GetItemById(Convert.ToInt32(itemId));

            //初始化工作的参数

            SPWorkflowActivationProperties initProp = new SPWorkflowActivationProperties();

            context.Initialize(initProp);

            SPGroup group = context.Web.SiteGroups[groupName];

            if (group == null)

                throw new Exception(String.Format("[{0}]不存在或被删除", groupName));

            string userLoginName = initProp.Originator.ToLower(); //流程启动人

            //判断流程启动人是否属于组

            foreach (SPUser user in group.Users)

            {

                if (user.LoginName.ToLower() == userLoginName) //排除系统管理员

                    return true;

            }

            return false;

        }

    }

}

Step2  编写配置文件。在CodeArt.ACTIONS文件中添加如下配置。

<Conditions And="" Or="" Not="" When="如果" Else="否则">

   <Condition Name="CodeArt_比较提交人所属组"

        FunctionName="CompareOriginatorGroup"

        ClassName="CodeArt.SharePoint.WorkflowActions. WorkflowConditions"

           Assembly="CodeArt.SharePoint.WorkflowActions, Version=1.0.0.0,

Culture=neutral, PublicKeyToken=8d0e2047bbdccb4d"

        AppliesTo="all"   

        UsesCurrentItem="true">

        <RuleDesigner Sentence="若提交人属于 %1 网站组">

             <FieldBind  Id="1" Field="_1_" DesignerType="TextArea" Text=""/>          

        </RuleDesigner>

        <Parameters>

             <Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />               

        </Parameters>

   </Condition>

</Conditions>

以上配置制定了条件名称,以及条件对象的方法。指定了采用TextArea(文本框)设计第一个额外参数(groupName)。将配置文件部署到12\TEMPLATE\2052\Workflow中,程序集部署到GAC后,在SharePoint Designer中就可以使用这个自定义的"比较提交人所属组"条件了,如图4所示。

自定义的条件

 

 

 

posted on 2009-07-23 21:33  KimhillZhang  阅读(1205)  评论(1编辑  收藏  举报