一步一步学习sharepoint2010 workflow 系列第三部分:自定义SharePoint代码工作流 第11章 自定义工作流活动和条件(Custom workflow activities and conditions)

本章涵盖

■构建自定义的叶活动

■构建自定义复合活动

■SharePoint Designer中发布活动

■SharePoint Designer中的编码自定义条件

 

 

  本质上说,活动是面向对象的类。在面向对象编程,你永远不会只有一个类去做一切事情。相反,你需要很多类结合品抽成一个应用程序。

这个方法使用工作流和活动。你绝不希望一个大的复杂的Visual Studio工作流有10000行代码在代码视图中。最好的办法是分离代码到自定义活动来管理工作流外部模板。工作流模板应该是被使用去工作流活动模型,并形象的显示工作流。自定义代码绝大多数需要被外部模板管理在自定义活动中。

使用自定义活动你将大大提高你的工作流的可读性和可维护性。从可读性角度上来看,考虑代码的活动。如果你记得,一个代码活动是使用去添加代码到一个工作流模板。这个活动是经常被滥用。例如,如果你有一个代码活动包含500行代码,你战胜工作流模板的价值。你不能形象的看到什么活动在做。如果你参加了这个活动的逻辑,并提取它到几个自定义活动,你不仅看到这些活动获得可读性的好处通过模板上的这些活动模型,代码也会更容易维护。 

你可以建立良种类型的自定义活动:叶活动和组合活动。虽然组合活动类似分支有很多叶子(子活动),叶活动只能包含特定的功能。一个组合活动包含许多叶活动,从某种意义上来说,在父工作流模板上的小型工作流,叶活动的例子包含有Delay,CreateTask,和SetState。组合活动包含While,IfElse和Sequence。 

活动可以被打包并发布到SharePoint Designer 和使用到Visual Studio工作流。部署到SharePoint Designer包括创建动作文件并部署它到文件系统。SharePoint Designer将下载这个文件当它远程连接到你的SharePoint站点,该文件会告诉SharePoint Designer哪个自定义动作提供给用户可用。 

自定义条件也可以被发布到SharePoint Designer。条件是使用去确定一个动作块执行与否。创建一个条件,你需要一个.NET类和一个返回布尔值的方法。你指向到你的方法在相同的动作文件,SharePoint designer将作为方法使用在SharePoint Designer工作流中。 

11.1 建立自定义叶活动  (Building custom leaf activities )

建立自定义叶活动是相对比较容易的。该过程设计创建一个类和扩展基本活动类。然后重写Execute方法,并开始编码!为了添加你的活动到工作流,生成项目,然后活动将出现在工具箱中。除此之外,你可以混合一些其他技术去使你的自定义活动更强大。这包括创建自定义属性将加载属性界面。有了这些属性,你也可以编写自定义验证和指定默认设置。你甚至可以定制你的活动主题通过改变活动的背景颜色当它被拖放到工作流模板。 

 11.1.1 自定义活动基本原理(Custom activity fundamentals)
在你准备进入高级知识点之前,让我们建立一个通用的自定义叶活动。要这到这点,你要去创建一个新的项目并添加一个扩展基本活动的新类。然后要重写Execute方法,然后生成并拖放你的活动到一个工作流模板。按照表11.1的步骤。

示例设置准备  

本部分是有意让事情变得简单。你要创建一个活动名字叫CreateSubSites,你将在后面的章节进行扩展以从工作流提供SubSites。这是一个自定义活动能有利的简单的示例,因为SubSite能被可重用跨越多个工作流。

表11.1

 

 

Listing 11.1 CreateSubSite 活动文件内容

 

 using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.ComponentModel;
using Microsoft.SharePoint;
namespace CustomActivities
{
public class CreateSubSite: Activity
{
protected override ActivityExecutionStatus Execute(
ActivityExecutionContext executionContext)
{
return ActivityExecutionStatus.Closed;
}
}
}

 

  表11.1续

 

 

很简单,不是吗?现在,你在移动到下一节之前,注意以下几点。你需要了解Execute方法的返回值,因为Execute不是你仅仅可以重写的方法,你需要去寻找其他可用的方法。 

  ActivityExecutionStatus 是Execute方法的返回类型因为运行时需要知道活动状态之后执行调用。表11.2显示一个可能的状态列表。

表11.2

 

 

11.1.2  添加依赖项属性和验证(Adding dependency properties and validation )

通过添加自定义属性到你的活动,你给使用者在设计工作流时指定值的能力。此外,自定义属性,活动将能在运行时来回传递值。对于你的CreateSubSite活动,你想去添加SiteName,SiteUrl,SiteTemplate和工作流上下文属性。你的属性添加后,用户建立工作流将可以在编辑器(图11.1)指定属性值。 

 图11.1

  你应该考虑两种类型的属性:标准的.NET属性和依赖项属性。如果你只想在属性窗格中(图11.1)提供一个属性,那么标准的.NET属性就可以了,使用者在建立工作流后在设计时能指定属性值。如果值是运行时才能知道,那么依赖项属性开发发挥作用。例如你希望一个活动传递输出一个值到另外一个活动的输入。这个值直到运行时才能知道接受活动的值。在此情况下,依赖项属性是必须的。

图11.2显示依赖项属性如何在属性绑定对话框下方显示。然后,你能绑定属性到活动进行传递。

 图11.2

  依赖项属性存储他们的值在一个hash控制通过工作流运行时而不是本地变量,像绝大数的.NET属性值。由于一个.NET属性能被绑定到任何地方的变量,这个依赖不能重复使用。依赖项属性,另一方面,是不依赖,因为他们总是引用相同的hash。没有比依赖属性更好的类型了,所有你建立在此节都将使用依赖属性。

 依赖属性代码类似标准的.NET属性,有几个例外。注意一个示例依赖属性在列表11.2

Listing 11.2 Sample dependency property 

public static DependencyProperty SiteUrlProperty =

System.Workflow.ComponentModel.DependencyProperty.
Register("SiteUrl"typeof(string), typeof(TestActivity));
[Description("Enter a relative URL for the new site")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SiteUrl
{
get
{
return ((string)(base.GetValue(TestActivity.SiteUrlProperty)));
}
set
{base.SetValue(TestActivity.SiteUrlProperty, value);
}

} 

 

  首先你将注意到你的依赖项属性对象SiteUrlProperty是通过DependencyProperty的注册方法注册工作流运行时的hash。 此外,注意标准.NET属性名为SiteUrl,但是它的getter和setter是引用SiteUrlProperty依赖项属性,而不是一个通用的字符串对象。

 现在回到你的自定义CreateSubSite活动,你想去添加四个自定义依赖项属性,这将帮助子站点的配置。

首先添加两个简单的字符串属性为这个站点的名字和URL。然后你需要一个属性去指定哪个站点模板被使用(例如,blog,wiki或者team site)。最后你需要一个属性将包含工作流上下文。工作流上下文包含有价值的信息像上下文的SPSite和SPWeb对象。这将使你避免硬编码任何的URL,你的活动可以完全通用。要添加这些属性,输入列表11.3的代码到CreateSubSite类的Execute方法上面。

 在你输入列表11.3的代码,添加一个using块System.ComponentModel和Microsoft.SharePoint.WorkflowActions,这样它会生成成功。 

Listing 11.3 SiteName, SiteUrl, SiteTemplate, and Context dependency properties 

 public enum SiteTemplates

{
BLOG,
MPS,
STS,
WIKI
};
public static DependencyProperty SiteNameProperty =
System.Workflow.ComponentModel.DependencyProperty.Register(
"SiteName"typeof(string), typeof(CreateSubSite));
public static DependencyProperty SiteUrlProperty =
System.Workflow.ComponentModel.DependencyProperty.Register(
"SiteUrl"typeof(string), typeof(CreateSubSite));
public static DependencyProperty SiteTemplateProperty =
System.Workflow.ComponentModel.DependencyProperty.Register
("SiteTemplate"typeof(SiteTemplates), typeof(CreateSubSite));
public static DependencyProperty __ContextProperty =
System.Workflow.ComponentModel.DependencyProperty.Register(
"__Context"typeof(WorkflowContext), typeof(CreateSubSite));
[Description("Enter a name for the new site")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SiteName
{
get
{
return ((string)(base.GetValue(CreateSubSite.SiteNameProperty)));
}
set
{
base.SetValue(CreateSubSite.SiteNameProperty, value);
}
}
[Description("Enter a relative URL for the new site")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SiteUrl
{
get
{
return ((string)(base.GetValue(CreateSubSite.SiteUrlProperty)));
}
set
{
base.SetValue(CreateSubSite.SiteUrlProperty, value);
}
}
[Description("Specify the site template that will be used.")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public SiteTemplates SiteTemplate
{
get
{
return ((SiteTemplates)(base.GetValue(
CreateSubSite.SiteTemplateProperty)));
}
set
{
base.SetValue(CreateSubSite.SiteTemplateProperty, value);
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(
CreateSubSite.__ContextProperty)));
}
set
{
base.SetValue(CreateSubSite.__ContextProperty, value);
}
}

 

  属性命名规则不能打破

context属性的名称是有两个下划线,__Context.你可能觉得这样有点古怪,但是它是必要的因为在本章后面你要发布这个活动到SharePoint Designer。SharePoint Designer寻找属性指定名称,__Context恰好是一个。当设计者看到这个属性,它保存WorkflowContext在这个属性。
你还能注意到所有静态依赖项属性最终如何拥有的属性名称。这是一个需求。还要注意在注册的第一个参数。这是运行时属性hash的关键。必须是属性名称。未能遵守这些命名规则,将导致运行时错误。

  当你输入列表代码到你的活动,再次生成项目。之后回到workflow1,点击CreateSubSite活动。注意你的四个属性显示在属性菜单(图11.3)随意指定一个站点名称和你喜欢的模板。

 图11.3

 

 现在,你可能会想__Context属性如何作。此属性必须被使用依赖项属性因为你不知道运行时属性的值。你获得WorkflowContext从ApplyActivation活动。按照表11.3的步骤去配置ApplyActivition活动去设置你的上下文。

   表11.3

 

 完成这些步骤之后,CreateSubSite活动在运行时将有一个workflowContext对象。如果你不受限使用ApplyActivation活动,__Context属性将是NULL。你可能已经注意到在ApplyActivation活动有另外的下划线属性名字叫__WorkflowProperties.另外一个令牌在SharePoint Designer看起来像_ _WorkflowProperties.__Context和__WorkflowProperties有类似对象像SPSite和SPWeb.你可能已经使用了WorkflowProperties对象在OnWorkflowActivated活动初始化时。如果你曾需要一个WorkflowContext对象,而不是一个WorkflowProperties对象,你就知道如何去配置ApplyActivation活动去初始化WorkflowContext属性。

在到下一节之前,让我们添加代码到我们的CreateSubSite类中。在属性的位置,我们添加代码将提供子站点。 输入列表11.4的代码到Execute方法。 

  Listing 11.4 Site provisioning code within the Execute method

SPSecurity.RunWithElevatedPrivileges(delegate()

{
using (SPSite site = new SPSite(__Context.Site.ID))
{
using (SPWeb web = site.AllWebs[__Context.Web.ID])
{
if (web.Webs.Names.Contains(SiteUrl))
throw new UrlAlreadyInUseException();
else
{
web.Webs.Add(
SiteUrl,
SiteName,
"desc.",
1033,
SiteTemplate.ToString(),
false,
false);
}
}
}
});

return ActivityExecutionStatus.Closed; 

 

在Execute方法,用户的权限必须先提升到服务账户。这是因为你不能保证,用户有必要的权限在SharePoint中创建子站点。伺候,你需要获得SPSite和SPWeb对象从你的上下文。SPWeb对象将被父站点的子站点创建。你创建了子站点之后,确认URL是否唯一。你不能有两个相同的URL站点。这将使用一个FaultHandingActivity去处理一个唯一命名异常类似UrlAlreadyInUseException(图11.4)在这条路线上,你可以把负担给工作流去知道如何处理异常。确认URL可用之后,该站点被提供。 

 图11.4

 

很容易的去创建一个自定义异常。创建一个新类并使用如下代码:

 public class UrlAlreadyInUseException : Exception

 

{
public UrlAlreadyInUseException()
{
}
}


   11.1.3  属性验证(Property validation)

   此时,不会很方便的添加一些验证到你的新创建的属性上?
  以SiteUrl属性为例。URL是敏感的属性因为如果输入一些不正确的条目站点集上的方法将抛出异常。如果你输入指定URL字符或者如果你不使用想对应的URL但是你放入一个全路径的URL如http://intranet/someurl.当你调用web.Webs.Add方法(列表11.4),URL必须是相对的,必须没有包含任何斜线或特殊字符。如果URL给定值不符合你的要求,让我们提高你的活动的验证能力以使在编译时抛出错误。

  要创建一个自定义验证,你需要去创建一个新类它扩展ActivityValidator。ActivityValidator类有一个方法叫Validate你需要重写。在此方法你能放置你希望的代码去确定如果属性被正确配置。此方法的返回类型是ValidationErrorCollection,一个ValidationError对象的集合。你可以使用ValidationErrors去填充此集合,Visual Studio将显示这些错误在错误界面当你生成项目并且生成失败时(图11.5).

 图11.5

 

Validate方法是你想要的程序。如果你想去验证5个属性,你可以写代码去写5个验证在一个Validate方法。另一方面,你可以写5个单独的验证器。你自己选择。使用表11.4的步骤为你的CreateSubSite活动创建一个验证器。 

  表11.4

 

 

Listing 11.5 UrlValidator class shell 

using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.ComponentModel.Compiler;
using System.Text.RegularExpressions;
namespace CustomActivities
{
public class UrlValidator: ActivityValidator
{
public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
{
return base.Validate(manager, obj);
}
}
}

 

表11.4续

 

 

Listing 11.6 Validate method code 

CreateSubSite createSubSite = obj as CreateSubSite;

ValidationErrorCollection errCol = new ValidationErrorCollection();
if (createSubSite != null)
{
string url = createSubSite.SiteUrl;
if (url != null && url.Trim() != string.Empty)
{
Regex noSpecialChars = new Regex(@"\W");
if (noSpecialChars.IsMatch(url))
{
errCol.Add(new ValidationError("The URL should be relative to the site, " +"with no slashes, spaces, or special symbols."0001));
}
}
if (url == string.Empty)
{
errCol.Add(new ValidationError("The SiteUrl property cannot be null"0002));
}
if (createSubSite.SiteName == string.Empty)
{
errCol.Add(new ValidationError("The SiteName property cannot be null"0003));
}
}

return errCol;  

 

 

 Validate代码首先获取一个CreateSubSite活动的引用。属性可以从这个对象中拉出。接下来,你检查去确保这个活动不会NULL,如果不是为NULL,你运行一个正则表达式针对URL,检查是否有特殊字符。如果有特殊字符被发现,一个错误消息被添加进集合。接下来,你确认SiteName和SiteName属性有没有值。因为SiteTemplate是一个枚举,而Context是建在运行时,它没有必要去验证这些属性。

续表11.4

 

 

 图11.6

 

 

  你完成了添加自定义属性和属性验证到你的自定义活动。然后,你的活动不是很美观,你是不设置任何默认值给你的属性。下一节将引导你通过自定义工具箱项以及你的参数设置默认值当用户拖放活动到工作流模板时。

11.1.4 活动工具箱项 (Activity toolbox items )

通过创建一个类扩展ActivityToolboxItem你可以轻松的定制活动在工具箱中的外观。你还可以分配默认值给你的自定义属性当用户拖放活动到工作流模板。这是简单的只需几行代码。 你在本节构建的ActivityToolboxItem很简单,但是当你获取复合活动这个类将变得复杂。按照表11.5的步骤去添加一个自定义ActivityToolboxItem到你的自定义活动。

表11.5

 

 

Listing 11.7 CreateSubSiteToolboxItem class 

[Serializable]

internal class CreateSubSiteToolboxItem : ActivityToolboxItem
{
public CreateSubSiteToolboxItem(Type type): base(type)
{
}
private CreateSubSiteToolboxItem(SerializationInfo serializeinfo,StreamingContext context)
{
this.Deserialize(serializeinfo, context);
this.Description = "Creates sub site below context web.";
this.Company = "SP WFs in Action";
this.DisplayName = "Create Sub Site";
this.Bitmap = new System.Drawing.Bitmap(CustomActivities.Properties.Resources.STSICON);
}
protected override IComponent[] CreateComponentsCore(IDesignerHost host)
{
CreateSubSite css = new CreateSubSite();
css.SiteName = "Contoso Team Site";
return new IComponent[]{css};
}
}

 

  注意首先你的代码标记成可序列化的。两个构造函数。第二个构造函数,你反序列化的类和分配项目的元数据值如描述,公司和图标。下面重写方法允许你去指定默认值给你的属性。它不是强制重写的方法,但它可以帮助你当你想预订值,这个方法就显得重要当你进入复合活动因为你会以编程方式添加活动到子活动。现在,你只需要返回活动本身,没有子活动的活动。

这时,你可以生成你的项目并测试工具箱项。首先你会注意当你获取工具箱既不是你的项目元数据也不是图标显示。如果你继续前进拖放活动到工作流模板,SiteName属性将被设置为Contoso Team Site。需要手动添加到工具箱。按照表11.5手动添加活动到工具箱。 

表11.5续

 

 

随着你的工具箱项显示自定义元数据和突变,你可以美化活动到工作流模板。你可以活动更改背景颜色和提供自定义图标的渲染。 

11.1.5  主题化你的活动(Theming your activity )

 通过主题化你的活动,你可以自定义值背景颜色,字体类型,字体颜色和图标。显然,这不是很关键的技术,但它肯定增加趣味和独特的本土活动。这个完成需要两个类,ActivityDesigner和ActivityDesignerTheme。此外,使用一个跨站点的主题,可以帮助用户感觉更舒适,当和新的工作流工作时。

当你创建一个自定义ActivityDesigner类你可以做几件事情到你的活动。例如,你可以更改活动的尺寸或者更改活动本身文本的放置。更妙的是,你可以添加自定义动词到你的活动。当你邮寄活动,你获得一个菜单包含属性按钮。其他熟悉的动词是生成处理,复制和警用。你可以添加自定义动词到这个菜单通过事件处理附加动词去运行自定义代码。 

 ActivityDesigner是主题需要的,而不是ActivityDesignerTheme类,因为一个活动有一个设计属性指向到ActivityDesigner。然后ActivityDesigner有一个ActivityDesignerTheme属性指向你的自定义主题。如果你想要做到的是主题化你的活动,ActivityDesigner类将为空。在ActivityDesignerTheme类构造函数里面,设置所有你的苏醒如BorderStyle,BackgroundStyle,和ForeColor。按照表11.6的步骤去配置你的活动的自定义主题。

 表11.6

 

 

 

Listing 11.8 CreateSubSiteActivityDesigner.cs in its entirety 

 

using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.ComponentModel.Design;
using System.Drawing;
namespace CustomActivities
{
[ActivityDesignerTheme(typeof(CreateSubSiteActivityDesignerTheme))]
public class CreateSubSiteActivityDesigner : ActivityDesigner
{
}
public class CreateSubSiteActivityDesignerTheme : ActivityDesignerTheme
{
public CreateSubSiteActivityDesignerTheme(WorkflowTheme theme): base(theme)
{
this.BackColorStart = Color.Orange;
this.BackColorEnd = Color.OrangeRed;
this.BackgroundStyle = System.Drawing.Drawing2D.
LinearGradientMode.Horizontal;
}
}
}

  现在已经征服了叶活动,现在可以到下一阶段。复合活动允许你创建包含子活动的活动。大部分信息是包含在叶活动一节作为复合活动的基础,你准备应付更先进的东西。

 

11.2 建立自定义组合活动 (Building custom composite activities )

复合活动是当你想创建可重用活动组是最有用的。例如,你可能有5个或5个活动一起动作,你希望一个父活动包含这些子活动,使他们可重复使用在其他的工作流。这是最常见使用的复合活动 。

另一种用途是为创建控制流活动。控制流的活动工作被叫做控制流复合活动。这包括活动迭代,例如While活动,和其他不迭代,例如IfElse和parallel(并行)活动。你可以创建一个自定义复合活动担任类似目的像循环(而不是While循环)。创建自定义控制流活动主题超过了本书的范围。本书为研究这些高级技术只集中在Windows workflow Foundation技术可能是一个好的选择。相反,让我们专注复合活动是比较常见的。如父和子复合为可重用的目的。 

复合活动扩展CompositeActivity类。While,IfElse,Sequence和parallel活动都是扩展CompositeActivity。当你想去创建一个自定义复合活动,你可以选择直接扩展CompositeActivity或者去扩展复合活动如sequence活动作为你的基类。后者是最常见的方法。 

图11.7所示,当你扩展一个顺序活动,你是留下一个看起来和顺序活动一样的活动在活动设计器上。那么它成为一个简单拖放过程,拖放你的子活动到自定义顺序活动里面,此后生成并重用这个活动在许多的工作流中。 

 图11.7

 

 在复合活动依赖项属性也发挥了作用。请注意图11.8的自定义顺序活动内的子活动显示挂锁图标。这是通知你,你不能编辑这些活动。如果你必须编辑复合活动包中的子活动属性,可以提升它们的属性到父复合活动,它们将显示在父复合活动属性矿口。你只能提升依赖项属性,不能是标准的.NET属性。

 图11.8

随着介绍的深入,让我们去建立一个自定义的复合活动。你的复合活动将包装你之前创建的CreateSubSite活动。此外创建一个子站点它将通知站点集管理员。按照表11.7的步骤创建这个复合活动。

表11.7

 

 

 

 

这就是IT人员!可以很容易的看到,建立一个可重复使用的复合活动并不困难。列表11.9显示CreateSitePlusNotify活动的整个代码。  

Listing 11.9 CreateSitePlusNotify activity code

/***************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.ComponentModel;
namespace CustomActivities
{
public class CreateSitePlusNotify : SequenceActivity
{
private Microsoft.SharePoint.WorkflowActions.ApplyActivation applyActivation1;
private CreateSubSite createSubSite1;
public Microsoft.SharePoint.WorkflowActions.WorkflowContext workflowContext = 

new Microsoft.SharePoint.WorkflowActions.WorkflowContext();
private Microsoft.SharePoint.WorkflowActions.SendEmail sendEmail1;
public static System.Workflow.ComponentModel.DependencyProperty
SiteUrlProperty = DependencyProperty.Register(
"SiteUrl"typeof(System.String),
typeof(CustomActivities.CreateSitePlusNotify));
public static System.Workflow.ComponentModel.DependencyProperty
SCAdminEmailProperty = DependencyProperty.Register(
"SCAdminEmail"typeof(System.String),
typeof(CustomActivities.CreateSitePlusNotify));
public CreateSitePlusNotify()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.CanModifyActivities = true;
System.Workflow.Runtime.CorrelationToken correlationtoken1 = new System.Workflow.Runtime.CorrelationToken();
System.Workflow.ComponentModel.ActivityBind activitybind1 = new System.Workflow.ComponentModel.ActivityBind();
System.Workflow.ComponentModel.ActivityBind activitybind2 = new System.Workflow.ComponentModel.ActivityBind();
System.Workflow.ComponentModel.ActivityBind activitybind3 = new System.Workflow.ComponentModel.ActivityBind();
this.sendEmail1 = new Microsoft.SharePoint.WorkflowActions.SendEmail();
this.createSubSite1 = new CustomActivities.CreateSubSite();
this.applyActivation1 = new Microsoft.SharePoint.WorkflowActions.ApplyActivation();
//
// sendEmail1
//
this.sendEmail1.BCC = null;
this.sendEmail1.Body = "Body";
this.sendEmail1.CC = null;
correlationtoken1.Name = "CreateToken";
correlationtoken1.OwnerActivityName = "CreateSitePlusNotify";
this.sendEmail1.CorrelationToken = correlationtoken1;
this.sendEmail1.From = null;
this.sendEmail1.Headers = null;
this.sendEmail1.IncludeStatus = false;
this.sendEmail1.Name = "sendEmail1";
this.sendEmail1.Subject = "Subject";
activitybind1.Name = "CreateSitePlusNotify";
activitybind1.Path = "SCAdminEmail";
this.sendEmail1.SetBinding(
Microsoft.SharePoint.WorkflowActions.SendEmail.ToProperty,
((System.Workflow.ComponentModel.ActivityBind)
(activitybind1)));
//
// createSubSite1
//
this.createSubSite1.@__Context = null;
this.createSubSite1.Name = "createSubSite1";
this.createSubSite1.SiteName = "Contoso Team Site";
this.createSubSite1.SiteTemplate =CustomActivities.CreateSubSite.SiteTemplates.STS;
activitybind2.Name = "CreateSitePlusNotify";
activitybind2.Path = "SiteUrl";
this.createSubSite1.SetBinding(
CustomActivities.CreateSubSite.SiteUrlProperty,
((System.Workflow.ComponentModel.ActivityBind)
(activitybind2)));
//
// applyActivation1
//
activitybind3.Name = "CreateSitePlusNotify";
activitybind3.Path = "workflowContext";
this.applyActivation1.@__WorkflowProperties = null;
this.applyActivation1.Name = "applyActivation1";
this.applyActivation1.SetBinding(
Microsoft.SharePoint.WorkflowActions.
ApplyActivation.@__ContextProperty,
((System.Workflow.ComponentModel.ActivityBind)
(activitybind3)));
//
// CreateSitePlusNotify
//
this.Activities.Add(this.applyActivation1);
this.Activities.Add(this.createSubSite1);
this.Activities.Add(this.sendEmail1);
this.Name = "CreateSitePlusNotify";
this.CanModifyActivities = false;
}
[System.ComponentModel.DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[System.ComponentModel.BrowsableAttribute(true)]
[System.ComponentModel.CategoryAttribute("Misc")]
public string SiteUrl
{
get
{
return ((string)(base.GetValue(CustomActivities.CreateSitePlusNotify.SiteUrlProperty)));
}
set
{
base.SetValue(CustomActivities.CreateSitePlusNotify.SiteUrlProperty, value);
}
}
[System.ComponentModel.DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[System.ComponentModel.BrowsableAttribute(true)]
[System.ComponentModel.CategoryAttribute("Misc")]
public string SCAdminEmail
{
get
{
return ((string)(base.GetValue(CustomActivities.CreateSitePlusNotify.SCAdminEmailProperty)));
}
set
{
base.SetValue(CustomActivities.CreateSitePlusNotify.SCAdminEmailProperty, value);
}
}
}

} 

 11.3 发布活动到SharePoint Designer (Publishing activities to SharePoint Designer)

毫无疑问,许多人建立SharePoint 工作流不会被程序员和将使用工具像SharePoint Designer替换。麻烦是他们往往将一直需要去满足复杂的业务需求通常只能被Visual Studio满足。如何缩小SharePoint Designer和Visual Studio之间的鸿沟?发布你的自定义活动到SharePoint Designer是最简单的方式能做到这点。

当SharePoint Designer连接到一个SharePoint站点,它首先到服务器,并从服务器上拉出动作扩展的文件。在这个文件或者文件集,SharePoint Designer知道什么动作和条件它需要使它可用给它的用户当他们开发工作流时。所有你(程序员)还要去做的是创建你自己的动作文件去获取你的自定义活动部署到SharePoint Designer。最后的任务是去使你的活动在应用程序的Web.config中标记为安全。按照表11.8的步骤去部署你的自定义CreateSubSite活动到SharePoint Designer。 

表11.8

 

 

Listing 11.10 CustomActivities.ACTIONS 

/*********************************************/

<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions Sequential="then" Parallel="and">
<Action Name="Create Sub Site"
ClassName
="CustomActivities.CreateSubSite"
Assembly
="CustomActivities, Version=1.0.0.0, Culture=neutral,PublicKeyToken=929a2d1b8d7f5b6c"
AppliesTo
="all"
Category
="Custom Actions">
<RuleDesigner Sentence="Create sub-site named %1
with the URL of %2 and using the %3 site template."
>
<FieldBind Field="SiteName" Text="name" Id="1" />
<FieldBind Field="SiteUrl" Text="url" Id="2" />
<FieldBind Field="SiteTemplate" DesignerType="Dropdown" Text="template" Id="3">
<Option Name="Blog" Value="BLOG"/>
<Option Name="Meeting Workspace" Value="MPS"/>
<Option Name="Team Site" Value="STS"/>
<Option Name="Wiki" Value="WIKI"/>
</FieldBind>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,

Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="SiteTemplate" Type="System.Enum, mscorlib" Direction="In" />
<Parameter Name="SiteName" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="SiteUrl" Type="System.String, mscorlib" Direction="In" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>

 

  你将注意到在这个列表中的动作有三个主要元素:一个Assembly,一个RuleDesigner和Properties.动作元素的属性弥补程序集引用去告诉SharePoint什么程序集包含你的自定义活动。注意如何引用你的自定义CustomActivities程序集和默认命名空间。还要注意在程序集引用的PublicKeyToken设置。确认指定的你自己的token。另外一个有趣的属性是Category属性。这个属性告诉Sharepoint Designer你的自定义活动是哪个动作分类(图11.9)。下一个主要分类元素是RuleDesigner元素。RuleDesigner元素有一个Sentence属性呈现给用户。你可以使用此Sentence从用户手机信息。在sentence,你可以添加标签在格式化比例 percent/id(例如%1).这个标记是映射到字段ID=1。SiteName字段有一个文本属性设置名称呈现蓝色的链接,当用户点击链接,它们将提示指定一个值为这个网站。这个值映射到parameters里面。哪些参数是映射到依赖项属性在.NET活动。这是如何通过SharePoint Designer用户传递到.NET活动指定的值。

 有一个连接点我们会很快找到。注意如何站点模板字段有一个DesignerType属性。你可以使用这个设计类型去控制如何你想用户去指定值。在这里的SiteTemplate,它呈现一个下拉列表弹出框通过硬编码选项。请参考表11.9的DesignerType的设置。

  表11.9

 

 

最后,还记得_ _Context依赖项属性你创建为你的CreateSubSite活动?你给它一个奇怪的名字因为SharePoint Designer查找一个依赖项属性的名字并很聪明的获取工作流的workflowContext对象并分配它到__Context参数。这里发现的其他选项在表11.10。 

 表11.10

 

随着你的ACTIONS文件放置,你将准备去发布你的活动。生成并部署项目。这将更新程序集并发布你的ACTIONS文件到服务器上的工作流文件夹。打开SharePoint Designer并连接到站点。创建一个新的工作流(任何类型),然后SharePoint designer将找到你的自定义ACTIONS文件来确定什么动作为你可用。你应该注意你的自定义动作在自定义动作种类中。图11.10显示你的动作看起来的样子。

 图11.10

 

 11.4  建立自定义条件为SharePoint Designer(Building custom conditions for SharePoint Designer)

你已经完成建立一个自定义动作为SharePoint Designer,但是如何自定义条件呢?为SharePoint Designer建立自定义条件可以引入能力到你的SharePoint Designer工作流。例如,你可以写一个自定义条件去检查外部数据源或者系统在执行活动块之前。最好的部分是它很容易做到!所有你要做的是写一个.NET方法返回一个布尔值,使该方法在SharePoint Designer的条件中可用通过添加元素到ACTIONS文件。 

要建立在你的CreateSubSite示例,你要去写一个自定义条件可以使用去检查URl的可用性。它是方便的,你可以创建一个subsite从SharePoint Designer工作流,但是你怎么知道你所指定的网址是否可用?一个subsite可能已经存在使用此URL。你的自定义活动将采取建议URL作为参数并确认站点没有使用此URL。如果没有,条件将返回true,否则将返回false。按照表11.11去创建并发布你的条件到SharePoint Designer。 

 表11.11

 

首先你将看到在SubSiteExists方法有三个参数你将需要。前三个参数是必须的,第四个是可选的,但是示例中需要。你检查如果当前站点有一个子站点的URL和传递到方法的相同。如果是return true,如果不是return false。接下来,你需要去做添加几个元素到你的ACTIONS文件。 

 续表11.11

 

 Listing 11.11 Conditions Element

/******************************************************/

<Conditions>
<Condition Name="If Sub Site Exists"
FunctionName
="SubSiteExists"
ClassName
="CustomActivities.SubSiteExistsCondition"
Assembly
="CustomActivities, Version=1.0.0.0, Culture=neutral,PublicKeyToken=60cb170cc12e2631"
AppliesTo
="all"
Category
="Custom Conditions">
<RuleDesigner Sentence="Sub Site exists with a relative url of %1">
<FieldBind Id="1" Field="_1_" Text="url" DesignerType="TextArea" />
</RuleDesigner>
<Parameters>
<Parameter Name="_1_" Type="System.String, mscorlib" Direction="In"/>
</Parameters>
</Condition>

</Conditions> 

 

 XML的条件几乎是相同的XML。目前只有一些小的差异。首先,除了程序集定义和命名空间,你也需要定义方法名称将执行你的条件。第二,注意你如何不再引用改字段的名称或者你希望看到什么URL。相反,你使用一个_1_令牌去告诉Designer去传递字符串到第一个选项参数。即时URL是第四个参数,它一直是第一个可选参数。其他参数遵循类似的模式_2_,_3_等等。

 这应该是最后一步。生成并部署你的项目,并创建一个新的工作流在SharePoint Designer(或者编辑现有的)。你将需要关闭并重新打开连接去获取最后的ACTIONS文件。当你完成后,添加If Sub Site Exists条件,并添加Else分支。在If分支里面,添加一个Logger去记录采取的URL。在Else分支,添加一个创建SubSite动作,并添加Logger记录站点成功创建。当你完成后,你的模板看起来像图11.11.发布并测试你的工作流。

 图11.11

 

 

本人声明

 
  本博客内的文字和图片除标明出处和转贴的外,均系本人原创作品,如转载或使用,不是用于盈利目的,尽请使用,但请注明引自这里和作者的名字;有商业用途的,请与本人联系,协商后再作使用,否则我将采取法律手段维护自己的利益。谢谢合作。meiweijun@163.com

 

 

 

 

 

posted on 2011-10-25 09:50  梅卫军  阅读(1522)  评论(0编辑  收藏  举报