zhoujun.g 发表于 2015-9-25 12:33:22

SharePoint Designer工作流的扩展

  1SharePoint Designer工作流的迁移
  SharePoint Designer设计工作流的功能是提供给最终用户使用的,就如同用SPD定制站点页面一样,并没有提供专门的流程迁移工具。
  最终用户可以直接在生产环境中设计流程。对开发人员来说,如果在开发环境中设计好了一个比较复杂的流程,需要迁移到生产环境,必须手工操作,可以参考如下步骤。
  Step1确保生产环境已经建好了相应的列表,并确保列表的配置跟开发环境完全一致,包括列表名称、列表的所有栏。
  Step2用SharePoint Designer打开生产环境的站点,新建一个与开发环境名称一样的流程,绑定到同名称的列表,直接保存。
  Step3SharePoint 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         ClassName和Assembly分别是动作类的全名和所在的程序集。
  l         AppliesTo指定动作是应用到文档库还是列表,允许的值为list和all。
  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"。

  图2新建工作流活动项目
  Step2添加Microsoft.SharePoint.dll和microsoft.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));
        
        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));
        
        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));
        
        public ArrayList  To
        {
              get
              {
                  return (ArrayList)base.GetValue(ToProperty);
              }
              set
              {
                  base.SetValue(ToProperty, value);
              }
        }
  To属性是一个ArrayList类型的集合,在SharePoint Designer进行设计的时候可以直接绑定到用户。同样,添加邮件的抄送(CC)、暗送(BCC)、主题(Subject)、内容(Body)4个属性代码如下所示。
  public static DependencyProperty CCProperty = DependencyProperty.Register("CC",
  typeof(ArrayList), typeof(MailWithTaskLinkActivity));
        //抄送
        
        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));
        //暗送
        
        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));
        //主题
        
        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));
        //邮件内容
        
        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;
                  //获取到列表项
                  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 类的To、CC、Subject、Body 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>
  与动作类的配置很类似,ClassName和Assembly分别是条件方法所在的类的全名和类所在的程序集,FunctionName是静态函数的函数名。实现条件的静态函数必须采用如下签名。
  public static bool SomeFunction(WorkflowContext context, string listId,
  int listItem, string var1,string var2)
  {
   //…
  }
  其中context、listId和listItem分别是工作流上下文数据、当前列表的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; //获取关联的列表
              //获取关联的列表项
              //SPListItem item = list.GetItemById(Convert.ToInt32(itemId));
              //初始化工作的参数
              SPWorkflowActivationProperties initProp = new SPWorkflowActivationProperties();
              context.Initialize(initProp);
              SPGroup group = context.Web.SiteGroups;
              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 网站组">
               <FieldBindId="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所示。

  图4自定义的条件
  
  
  
页: [1]
查看完整版本: SharePoint Designer工作流的扩展