65.9K
CodeProject 正在变化。 阅读更多。
Home

工作流(SharePoint 2013、2010)使用沙盒解决方案自定义操作

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2015 年 8 月 24 日

CPOL

6分钟阅读

viewsIcon

34859

downloadIcon

285

如何在本地 SharePoint (2013 或 2010) 中创建沙盒工作流程操作

引言

在 SharePoint 工作流中,SharePoint Designer UI 提供了许多可用的操作。在某些情况下,这些操作可能不符合您的业务需求。那么该怎么办?您需要编写自定义操作来自动化您的业务流程。在本文中,我们将探讨如何创建自定义操作。

构建自定义操作的方法

我们可以通过两种方式开发自定义操作:自定义声明性操作沙盒操作。让我们来讨论这两种操作的区别。

自定义声明性操作:创建自定义声明性操作时,您会在 Visual Studio 中找到模板。您可以使用左侧工具箱中所有现有的操作。您在此处不能编写任何自定义代码。如果您需要任何自定义代码,则必须使用 Web 服务,因为您可以从自定义活动调用 Web 服务。

沙盒操作:自定义沙盒操作允许您编写自定义代码。在本文中,我的目标是向您展示如何创建自定义沙盒操作。我不会详细介绍此部分,因为在整篇文章中,我们将对此进行讨论。

自定义沙盒操作的内部

现在,定义一个将由自定义沙盒操作解决的业务问题。假设我们有一个名为项目列表的列表。项目列表有一个名为相关标书的列。相关标书列是选择类型。我们还有一个名为标书列表的列表。标书列表有一个名为标书编号的列,它是单行文本类型。要求是:每当用户在标书列表中添加新项目时,标书编号将作为新选项添加到相关标书列中。

Requirement

我想您已经猜到我将要做什么了。我将创建一个名为“更新选项”的自定义操作。然后,我将为标书列表创建一个工作流,该工作流将在添加新项目时触发。让我们在实际操作中了解更多。我将尝试开发以下操作。

designer action

在 Visual Studio 中,创建一个 SharePoint 空项目,并选择以沙盒解决方案的形式部署。

sandbox

deploy as

现在重命名功能,然后双击打开它。将作用域从 Web 改为 Site。由于它是沙盒解决方案,因此我们必须在站点中激活它,然后才能在 SharePoint Designer 中使用。

feature_rename

注意:不要忘记更改作用域。如果您忘记更改它,则在 SharePoint Designer 中找不到它。

基本上,沙盒操作包含两个主要部分:Element XML 和一个代码隐藏的.cs类。首先,添加Element XML 类。

右键单击您的项目,然后从模板中添加“空元素”,并为其提供一个有意义的名称。

element_xml

现在打开您的Element XML 文件,它应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
</Elements>

现在,将WorkflowActions标签作为Elements标签的内部标签。然后,将Action标签作为WorkflowActions的内部标签。

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <WorkflowActions>
    <Action Name="Update Any Choice Field"
            SandboxedFunction="true"
            Assembly="$SharePoint.Project.AssemblyFullName$"
            ClassName="UpdateChoice.UpdateChoice"
            FunctionName="UpdateChoiceValues"
            AppliesTo="all"
            Category="Ak Soultions">
    </Action>
  </WorkflowActions>
</Elements>

让我们讨论Action标签的属性。

  • Name:这是您的Action的名称。您可以选择任何有意义的名称。
  • SandboxedFunction:在我们的情况下,它是true,因为我们不打算在场级别执行任何操作。如果您需要在场级别执行任何操作,则可以将其设置为false
  • Assembly:这是此项目的程序集引用。在我们的情况下,值为$SharePoint.Project.AssemblyFullName$
  • ClassName:这是我们的代码隐藏类的名称。提供其名称有一个特定的格式。第一部分提供您的命名空间名称,后跟一个点(.),然后是我们的类名。假设您的命名空间是XXX,类是YYY。因此,格式变为XXX.YYY
  • FunctionName:执行操作时将执行的函数。此函数的签名必须是Hashtable
  • AppliesTo:我们可以将其值设置为all,以便此操作可用于所有子站点。
  • Category:您可以为自定义操作指定一个名称来对其进行分类。如果类别名称不存在,则将创建新类别。否则,此操作将添加到现有类别中。

因此,我们发现需要一个带有函数的代码隐藏类。让我们在项目中添加一个类,并在我们的类中添加一个返回Hashtable的函数。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint;
using Microsoft.SharePoint.UserCode;
using Microsoft.SharePoint.Workflow;
using System.Collections;
using System.Collections.Specialized;
namespace UpdateChoice
{
    public class UpdateChoice
    {
        public Hashtable UpdateChoiceValues()
        {
            var hash = new Hashtable();
            return hash;
        }
    }
}

现在,再次打开我们的Element XML 文件,并在Action标签内添加RuleDesigner标签。在RuleDesigner标签内,我们需要绑定所有要作为操作参数传递的字段。

 <RuleDesigner Sentence="Update Choice Field with the value: %1, 
 Field Name(internal name): %2, List Name: %3, Site Url: %4.">
        <FieldBind Field="Value" Text="Value" 
        Id="1" DesignerType="TextBox" />
        <FieldBind Field="FieldName" Text="Field Name" 
        Id="2" DesignerType="TextBox" />
        <FieldBind Field="ListName" Text="List Name" 
        Id="3" DesignerType="TextBox" />
        <FieldBind Field="SiteUrl" Text="Site Url" 
        Id="4" DesignerType="TextBox" />
 </RuleDesigner>

RuleDesigner标签的Sentence属性将与我们的自定义操作一起显示。为了实现上述场景,我声明了四个字段。

  • Value:此字段将作为Choice添加到相关标书列中。根据上述场景,它应该是标书编号的值。
  • FieldName:我们要更新哪个列。从设计器中,我们必须提供该列的内部名称。根据上述场景,它应该是Related_x0020_Tender
  • ListName & SiteUrl:我们需要在此处传递列表名称,因为没有它,我们将无法更新Choice列。还需要SiteUrl,因为列表可能属于其他上下文。我们将在代码隐藏类中看到更多内容。

对于每个字段,我们必须提供四个属性,例如FieldIdTextDesignerType

  • Field:字段名称。它应该是唯一的。
  • Id:每个字段都必须有一个唯一的 ID。我们可以通过提供%[Id Number]来将其链接到Sentence属性。
  • Text:此文本将在 SharePoint Designer UI 中显示。
  • DesignerType:您希望如何显示此字段。

现在,我们必须定义实际参数。为此,在Action标签内添加Parameters标签。

<Parameters>
        <Parameter Name="__Context" 
        Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, 
        Microsoft.SharePoint.WorkflowActions" 
        Direction="In" DesignerType="Hide" />
        <Parameter Name="Value" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="FieldName" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="ListName" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="SiteUrl" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
</Parameters>

实际上,您必须在此处指定我们要传递给此操作的字段的TypeDirection。Type 可以是StringIntGuid等。Direction可以有两个值:InOutIn表示您只需要将此值传递给操作。您不需要从操作中返回任何值。Out用于需要从操作中返回某些值的情况。第一个名为__Context的参数是特殊类型参数。我们始终必须提供它。它将是此工作流运行时所在的当前上下文。DesignerType将是Hide,因为我们不希望在操作中显示它。

现在,更新我们的UpdateChoiceValues函数。

 public Hashtable UpdateChoiceValues(SPUserCodeWorkflowContext context, 
 string Value, string FieldName, string ListName, string SiteUrl)
        {
            var hash = new Hashtable();
            using (SPSite site = new SPSite(context.CurrentWebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        hash["result"] = UpdateChoiceAction
                        (Value, FieldName, ListName, SiteUrl);
                        SPWorkflow.CreateHistoryEvent(web, context.WorkflowInstanceId, 0, 
                        web.CurrentUser, TimeSpan.Zero, "Information", "done" + Value, String.Empty);
                    }
                    catch (Exception ex)
                    {
                        hash["result"] = ex.InnerException.Message;
                        SPWorkflow.CreateHistoryEvent(web, context.WorkflowInstanceId, 0, 
                        web.CurrentUser, TimeSpan.Zero, "Error", ex.Message, String.Empty);
                    }
                }
            }
            return hash;
        }

在这里,我将所有Parameter标签作为UpdateChoiceValues函数的参数,以便我们可以使用这些参数编写业务逻辑。我编写了另一个名为UpdateChoiceAction的函数来实现上述场景。

private string UpdateChoiceAction(string value, string fieldName, string listName, string siteUrl)
        {
            try
            {
                using (SPSite site = new SPSite(siteUrl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        web.AllowUnsafeUpdates = true;
                        SPList spList = web.Lists[listName];
                        SPFieldChoice spChoiceField = (SPFieldChoice)spList.Fields[fieldName];
                        spChoiceField.AddChoice(value);
                        spChoiceField.Update();
                        web.AllowUnsafeUpdates = false;
                        return "Field has been updated with new value: " + value;
                    }
                }
            }
            catch (Exception ex)
            {
                return "Error: " + ex.Message;
            }
        }

现在,让我们看看我的整个代码。

Element.xml

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <WorkflowActions>
    <Action Name="Update Any Choice Field"
            SandboxedFunction="true"
            Assembly="$SharePoint.Project.AssemblyFullName$"
            ClassName="UpdateChoice.UpdateChoice"
            FunctionName="UpdateChoiceValues"
            AppliesTo="all"
            Category="Ak Soultions">
      <RuleDesigner Sentence="Update Choice Field with the value: %1, 
      Field Name(internal name): %2, List Name: %3, Site Url: %4.">
        <FieldBind Field="Value" Text="Value" 
        Id="1" DesignerType="TextBox" />
        <FieldBind Field="FieldName" Text="Field Name" 
        Id="2" DesignerType="TextBox" />
        <FieldBind Field="ListName" Text="List Name" 
        Id="3" DesignerType="TextBox" />
        <FieldBind Field="SiteUrl" Text="Site Url" 
        Id="4" DesignerType="TextBox" />
      </RuleDesigner>
      <Parameters>
        <Parameter Name="__Context" 
        Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, 
        Microsoft.SharePoint.WorkflowActions" Direction="In" 
        DesignerType="Hide" />
        <Parameter Name="Value" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="FieldName" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="ListName" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
        <Parameter Name="SiteUrl" Type="System.String, 
        mscorlib" Direction="In" DesignerType="ParameterNames" />
      </Parameters>
    </Action>
  </WorkflowActions>
</Elements>

UpdateChoice.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint;
using Microsoft.SharePoint.UserCode;
using Microsoft.SharePoint.Workflow;
using System.Collections;
using System.Collections.Specialized;
namespace UpdateChoice
{
    public class UpdateChoice
    {
        public Hashtable UpdateChoiceValues(SPUserCodeWorkflowContext context, 
        string Value, string FieldName, string ListName, string SiteUrl)
        {
            var hash = new Hashtable();
            using (SPSite site = new SPSite(context.CurrentWebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        hash["result"] = UpdateChoiceAction
                        (Value, FieldName, ListName, SiteUrl);
                        SPWorkflow.CreateHistoryEvent(web, context.WorkflowInstanceId, 0, 
                        web.CurrentUser, TimeSpan.Zero, "Information", "done" + Value, String.Empty);
                    }
                    catch (Exception ex)
                    {
                        hash["result"] = ex.InnerException.Message;
                        SPWorkflow.CreateHistoryEvent(web, context.WorkflowInstanceId, 0, 
                        web.CurrentUser, TimeSpan.Zero, "Error", ex.Message, String.Empty);
                    }
                }
            }
            return hash;
        }
        private string UpdateChoiceAction(string value, string fieldName, string listName, string siteUrl)
        {
            try
            {
                using (SPSite site = new SPSite(siteUrl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        web.AllowUnsafeUpdates = true;
                        SPList spList = web.Lists[listName];
                        SPFieldChoice spChoiceField = (SPFieldChoice)spList.Fields[fieldName];
                        spChoiceField.AddChoice(value);
                        spChoiceField.Update();
                        web.AllowUnsafeUpdates = false;
                        return "Field has been updated with new value: " + value;
                    }
                }
            }
            catch (Exception ex)
            {
                return "Error: " + ex.Message;
            }
        }
    }
}

现在,部署此解决方案,并在Ak Solutions类别下找到此操作。

action

附注:在 SharePoint 2013 工作流中,您找不到此操作。因此,请创建 2010 工作流来使用此类自定义操作。如果我们需要在 2013 工作流中使用呢?那么解决方案非常简单!创建 2010 工作流,然后从 2013 工作流运行它。我的意思是您必须创建两个工作流。

结论

您也可以将此部署到 SharePoint Online,因为它是一个沙盒解决方案。为此,请转到您顶级站点的站点设置。然后在Web Designer Galleries下单击Solutions。现在上传您的.wsp文件并激活它。关于编写自定义操作就说到这里。随时欢迎您提出反馈意见。

© . All rights reserved.