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





5.00/5 (3投票s)
如何在本地 SharePoint (2013 或 2010) 中创建沙盒工作流程操作
引言
在 SharePoint 工作流中,SharePoint Designer UI 提供了许多可用的操作。在某些情况下,这些操作可能不符合您的业务需求。那么该怎么办?您需要编写自定义操作来自动化您的业务流程。在本文中,我们将探讨如何创建自定义操作。
构建自定义操作的方法
我们可以通过两种方式开发自定义操作:自定义声明性操作和沙盒操作。让我们来讨论这两种操作的区别。
自定义声明性操作:创建自定义声明性操作时,您会在 Visual Studio 中找到模板。您可以使用左侧工具箱中所有现有的操作。您在此处不能编写任何自定义代码。如果您需要任何自定义代码,则必须使用 Web 服务,因为您可以从自定义活动调用 Web 服务。
沙盒操作:自定义沙盒操作允许您编写自定义代码。在本文中,我的目标是向您展示如何创建自定义沙盒操作。我不会详细介绍此部分,因为在整篇文章中,我们将对此进行讨论。
自定义沙盒操作的内部
现在,定义一个将由自定义沙盒操作解决的业务问题。假设我们有一个名为项目列表的列表。项目列表有一个名为相关标书
的列。相关标书
列是选择类型。我们还有一个名为标书列表的列表。标书列表有一个名为标书编号
的列,它是单行文本类型。要求是:每当用户在标书列表中添加新项目时,标书编号
将作为新选项添加到相关标书
列中。
我想您已经猜到我将要做什么了。我将创建一个名为“更新选项”的自定义操作。然后,我将为标书列表创建一个工作流,该工作流将在添加新项目时触发。让我们在实际操作中了解更多。我将尝试开发以下操作。
在 Visual Studio 中,创建一个 SharePoint 空项目,并选择以沙盒解决方案的形式部署。
现在重命名功能,然后双击打开它。将作用域从 Web 改为 Site。由于它是沙盒解决方案,因此我们必须在站点中激活它,然后才能在 SharePoint Designer 中使用。
注意:不要忘记更改作用域。如果您忘记更改它,则在 SharePoint Designer 中找不到它。
基本上,沙盒操作包含两个主要部分:Element
XML 和一个代码隐藏的.cs类。首先,添加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
,因为列表可能属于其他上下文。我们将在代码隐藏类中看到更多内容。
对于每个字段,我们必须提供四个属性,例如Field
、Id
、Text
和DesignerType
。
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>
实际上,您必须在此处指定我们要传递给此操作的字段的Type
和Direction
。Type 可以是String
、Int
、Guid
等。Direction
可以有两个值:In
或Out
。In
表示您只需要将此值传递给操作。您不需要从操作中返回任何值。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类别下找到此操作。
附注:在 SharePoint 2013 工作流中,您找不到此操作。因此,请创建 2010 工作流来使用此类自定义操作。如果我们需要在 2013 工作流中使用呢?那么解决方案非常简单!创建 2010 工作流,然后从 2013 工作流运行它。我的意思是您必须创建两个工作流。
结论
您也可以将此部署到 SharePoint Online,因为它是一个沙盒解决方案。为此,请转到您顶级站点的站点设置。然后在Web Designer Galleries下单击Solutions。现在上传您的.wsp文件并激活它。关于编写自定义操作就说到这里。随时欢迎您提出反馈意见。