Custom activity di creazione sito (SharePoint Designer)
L'esempio che segue illustra come creare una Custom Activity e Custom Condition per il workflow da usare con SharePoint Designer 2007.
In questo caso la custom activity permette di creare un sito basato su un determinato template specificando: titolo, nome, descrizione e titolo template (o nome template).
I passi da seguire sono:
La classe che gestisce la custom activity
Da notare come le property vengono bindate alla descrizione presente nel file xml e il metodo Execute che implementa la custom activity (DependencyProperty).
Il file xml di configurazione (Sgart.SharePoint.WF.ACTIONS)
Dove Action descrive quale classe implementa la custom activity, RuleDesigne indica a SharePoint Designer come disegnare l'activity e Parameter descrive i parametri della activity. In particolare i primi tre parametri (__Context, __ListId e __ListItem) sono gestiti in automatico dal workflow.
La riga per registrare la dll come safe
La riga per registrare la dll come activity del workflow (web.config)
Attivarlo nella web application interessata. A questo punto è possibile utilizzare la nuova activity in SharePoint Designer 2007.
Se si vuole creare anche una custom condition, i passi sono i seguenti:
I metodi IF per le conditions
e il relativo file xml
In questo caso notare il riferimento hai parametri posizionale (Filed="_1_") e non tramite nome.
Nel file sgart-sharepoint-wf.zip c'è sia il codice che il file di installazione (setup.exe) con i file xml sia in italiano (1040) che in inglese (1033).
Se si apportano modifiche, compilare il progetto, eseguire MakeSolution.bat per ricreare il file di solution (WSP), installarlo con setup.exe e attivare la feature nella web application.
Per usare la nuova Custom Activity in SharePoint Designer 2007:
In questo caso la custom activity permette di creare un sito basato su un determinato template specificando: titolo, nome, descrizione e titolo template (o nome template).
I passi da seguire sono:
- creare una classe che eredita da System.Workflow.ComponentModel.Activity
- creare un file xml che descrive la activity, da posizionare in \12\TEMPLATE\1033\Workflow\<nomeFile>.ACTIONS
- registrare la dll come safe control nel web.config (configuration\SharePoint\SafeControls\SafeControl)
- aggiungere la dll nel web.config nella posizione configuration\System.Workflow.ComponentModel.WorkflowCompiler\authorizedTypes\authorizedType
- andare nella central administration ed attivare la feature sulla web application (http://<serverUrl>:<port>/_admin/ManageWebAppFeatures.aspx)
La classe che gestisce la custom activity
C#: AddSPWeb.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;
namespace Sgart.SharePoint.WF
{
/// <summary>
/// add a new site (SPWeb)
/// </summary>
public class AddSPWeb : System.Workflow.ComponentModel.Activity
{
#region Constructors
// Methods
public AddSPWeb()
{
this.InitializeComponent();
}
#endregion
#region Initialize
private void InitializeComponent()
{
base.Name = "AddSPWeb";
base.Description = "AddSPWeb";
}
#endregion
#region Properties
[Description("Workflow Context")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(__ContextProperty)));
}
set
{
base.SetValue(__ContextProperty, value);
}
}
public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(AddSPWeb));
[Description("ListItem")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public int __ListItem
{
get
{
return ((int)(base.GetValue(__ListItemProperty)));
}
set
{
base.SetValue(__ListItemProperty, value);
}
}
public static DependencyProperty __ListItemProperty = DependencyProperty.Register("__ListItem", typeof(int), typeof(AddSPWeb));
[Description("Id of the list")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public string __ListId
{
get
{
return ((string)(base.GetValue(__ListIdProperty)));
}
set
{
base.SetValue(__ListIdProperty, value);
}
}
public static DependencyProperty __ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(AddSPWeb));
[Description("Title of the site to be created")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public string SiteTitleField
{
get
{
return ((string)(base.GetValue(SiteTitleFieldProperty)));
}
set
{
base.SetValue(SiteTitleFieldProperty, value);
}
}
public static DependencyProperty SiteTitleFieldProperty = DependencyProperty.Register("SiteTitleField", typeof(string), typeof(AddSPWeb));
[Description("Name of the site to be created")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public string SiteNameField
{
get
{
return ((string)(base.GetValue(SiteNameFieldProperty)));
}
set
{
base.SetValue(SiteNameFieldProperty, value);
}
}
public static DependencyProperty SiteNameFieldProperty = DependencyProperty.Register("SiteNameField", typeof(string), typeof(AddSPWeb));
[Description("Description of the site to be created")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public string SiteDescriptionField
{
get
{
return ((string)(base.GetValue(SiteDescriptionFieldProperty)));
}
set
{
base.SetValue(SiteDescriptionFieldProperty, value);
}
}
public static DependencyProperty SiteDescriptionFieldProperty = DependencyProperty.Register("SiteDescriptionField", typeof(string), typeof(AddSPWeb));
[Description("Name of the template")]
[Category("Custom")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
public string TemplateName
{
get
{
return ((string)(base.GetValue(TemplateNameProperty)));
}
set
{
base.SetValue(TemplateNameProperty, value);
}
}
public static DependencyProperty TemplateNameProperty = DependencyProperty.Register("TemplateName", typeof(string), typeof(AddSPWeb));
#endregion
#region Activity
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
//get current web and language
SPWeb webCurrent = __Context.Web;
uint lcid = webCurrent.Language;
//get selected template
SPWebTemplateCollection templates = __Context.Site.GetWebTemplates(lcid);
SPWebTemplate template = null;
try
{
//try with internal template Name (STS#1)
template = templates[this.TemplateName];
}
catch
{
//else search template by Title (Blank Site)
foreach (SPWebTemplate wt in templates)
{
if (wt.IsHidden == false
&& wt.Title.Equals(this.TemplateName, StringComparison.InvariantCultureIgnoreCase) == true)
{
template = wt;
break;
}
}
}
// Create the new site
SPWeb newWeb = webCurrent.Webs.Add(this.SiteNameField
, this.SiteTitleField, this.SiteDescriptionField
, template.Lcid, template, false, false);
// This activity has finished its job.
return ActivityExecutionStatus.Closed;
}
#endregion
}
}
Il file xml di configurazione (Sgart.SharePoint.WF.ACTIONS)
XML
<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions Sequential="then" Parallel="and">
<Action Name="Sgart - Create Web"
ClassName="Sgart.SharePoint.WF.AddSPWeb"
Assembly="Sgart.SharePoint.WF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53a6df0e63086949"
AppliesTo="list"
Category="Sgart"
UsesCurrentItem="true">
<RuleDesigner Sentence="Create a new site called %1 by template %4 at url %2 with description %3">
<FieldBind Field="SiteTitleField" Text="Site Title" Id="1" DesignerType="TextArea"/>
<FieldBind Field="SiteNameField" Text="Site Name" Id="2" DesignerType="TextArea"/>
<FieldBind Field="SiteDescriptionField" Text="Site Description" Id="3" DesignerType="TextArea"/>
<FieldBind Field="TemplateName" Text="Template Name" Id="4" DesignerType="TextArea"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" />
<Parameter Name="__ListId" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction="In" />
<Parameter Name="SiteTitleField" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="SiteNameField" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="SiteDescriptionField" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="TemplateName" Type="System.String, mscorlib" Direction="In" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>
La riga per registrare la dll come safe
XML: web.config
<SafeControl Assembly="Sgart.SharePoint.WF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53a6df0e63086949"
Namespace="Sgart.SharePoint.WF" TypeName="*" Safe="True" />
La riga per registrare la dll come activity del workflow (web.config)
XML
<authorizedType Assembly="Sgart.SharePoint.WF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53a6df0e63086949"
Namespace="Sgart.SharePoint.WF" TypeName="*" Authorized="True" />
Attivarlo nella web application interessata. A questo punto è possibile utilizzare la nuova activity in SharePoint Designer 2007.
Se si vuole creare anche una custom condition, i passi sono i seguenti:
- creare un metodo che ritorna un boolean
- aggiungere la descrizione della condizione al file xml (Sgart.SharePoint.WF.ACTIONS)
I metodi IF per le conditions
C#: IfSPWeb.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WorkflowActions;
namespace Sgart.SharePoint.WF
{
public class IfSPWeb
{
public static bool IfNotExistSPWebWithTitle(WorkflowContext context, string listId, int listItem, string title)
{
bool ok = true;
SPWeb webCurrent = context.Web;
foreach (SPWeb web in webCurrent.Webs)
{
try
{
if (web.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase) == true)
{
ok = false;
break;
}
}
catch{}
finally
{
web.Dispose();
}
}
return ok;
}
public static bool IfNotExistSPWebWithName(WorkflowContext context, string listId, int listItem, string name)
{
bool ok = true;
SPWeb webCurrent = context.Web;
string[] names = webCurrent.Webs.Names;
foreach (string n in names)
{
if (n.Equals(name, StringComparison.InvariantCultureIgnoreCase) == true)
{
ok = false;
break;
}
}
return ok;
}
}
}
e il relativo file xml
XML
<WorkflowInfo Language="en-us">
<Conditions And="and" Or="or" Not="not" When="If" Else="Else if">
<Condition Name="Sgart - Check if NOT exist site with Title"
FunctionName="IfNotExistSPWebWithTitle"
ClassName="Sgart.SharePoint.WF.IfSPWeb"
Assembly="Sgart.SharePoint.WF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53a6df0e63086949"
AppliesTo="list"
UsesCurrentItem="true">
<RuleDesigner Sentence="not exist site with title %1">
<FieldBind Field="_1_" Text="Site Title" Id="1" DesignerType="TextArea"/>
</RuleDesigner>
<Parameters>
<Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />
</Parameters>
</Condition>
<Condition Name="Sgart - Check if NOT exist site with Name"
FunctionName="IfNotExistSPWebWithName"
ClassName="Sgart.SharePoint.WF.IfSPWeb"
Assembly="Sgart.SharePoint.WF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53a6df0e63086949"
AppliesTo="list"
UsesCurrentItem="true">
<RuleDesigner Sentence="not exist site with name %1">
<FieldBind Field="_1_" Text="Site Name" Id="1" DesignerType="TextArea"/>
</RuleDesigner>
<Parameters>
<Parameter Name="_1_" Type="System.String, mscorlib" Direction="In" />
</Parameters>
</Condition>
</Conditions>
</WorkflowInfo>
Nel file sgart-sharepoint-wf.zip c'è sia il codice che il file di installazione (setup.exe) con i file xml sia in italiano (1040) che in inglese (1033).
Se si apportano modifiche, compilare il progetto, eseguire MakeSolution.bat per ricreare il file di solution (WSP), installarlo con setup.exe e attivare la feature nella web application.
Per usare la nuova Custom Activity in SharePoint Designer 2007:
- dal designer aprire il sito
- andare in File \ New
- selezionare SharePoint Content
- scegliere workflow e premere ok
- dare un nome al workflow, selezionare una lista e scegliere una modalità di avvio
- premere next e selezionare l'action Sgart - Crate Web
- compilare tutti i campi richiesti dall'activity e premere finish
The list of workflow actions on the server references an assembly that does not exist. Some actions will not be available. The assembly strong name is [...]. Contact your server administrator for more information.
Se ti compare questo errore nel disigner, il motivo sembra scontato, ovvero non viene trovato l'assembly... ma non è così. In realtà il messaggio è da interpretate come "c'è qualche cosa che non va quando il workflow cerca di instanziare la classe", questo qualche cosa può essere:- l'assembly non è registrato nel web.config come sicuro (strong)
- il costruttore non va bene
- l'xml di definizione non è corretto
- una proprietà (DependencyProperty ) della classe non è corettamente definita (ad esempio il parametro dell'ultimo typeof non si riferisce alla classe corrente)
- e... qualunque altro errore :-(