This post is about creating Workflow Associations declaratively using a CAML element declaration in a feature.
Workflow associations can be created using code (and the SharePoint UI of course), and there are plenty of articles on the interwebz demonstrating this, today, while trolling through an exported site template, I discovered that you can create Workflow Associations using CAML and this isn’t so well covered.
The CAML schema element we’re interested in is called, not surprisingly, WorkflowAssociation
and can only be included in a Web scoped feature.
<WorkflowAssociation Name="Workflow Asociation Name" Description="Workflow Asociation Description." Id="{f37372af-7244-4213-891a-7776d3db88bd}" BaseTemplateId="{faccf888-fbc2-4a61-bc79-74e23fc0f995}" ParentContentType="ContentType" ParentList="Lists/Tasks" ParentWeb="TRUE" TaskList="Lists/Tasks" TaskListTitle="Tasks" HistoryList="Lists/Workflow History" HistoryListTitle="Workflow History" StatusColumnShown="TRUE" InternalNameStatusField="WFAssociationName" RequireManagePermission="ViewListItems, EditListItems" Configuration="71761" AssociationData="{url encoded association data}" ContentTypeName="Automatic Task" ContentTypeId="0x...." />
Attributes
Attribute | Description |
---|---|
Name | The name of the workflow association |
Description | The Workflow association description |
Id | A Guid, the unique ID of this association |
BaseTemplateId | A Guid, this is the Guid ID of the Workflow template which is being associated, and can be found in the BaseID attribute of the Template element of the Workflow.xoml.wfconfig.xml file. |
ParentContentType |
|
ParentList | The SPWeb relative URL of the list in which the workflow association should be made. If ParentContentType is ContentType and this attribute is empty, the workflow association is made with the site ContentType. If ParentContentType is ContentType and this attribute is not empty, the workflow association is made with the list ContentType. If ParentContentType is List this attribute cannot be empty, and the workflow association is made with the List. If ParentContentType is Web this attribute is ignored. |
ParentWeb | Unknown ~ but always seems to be set to TRUE, as far as I can tell this attribute is ignored and not used at all. |
TaskList | The SPWeb relative URL of the workflow Tasks list, when the association is provisioned this list must exist. |
TaskListTitle | The Title of the workflow Tasks list. |
HistoryList | The SPWeb relative URL of the workflow History list, when the association is provisioned this list must exist. |
HistoryListTitle | The Title of the workflow History list. |
StatusColumnShown | TRUE | FALSE, this value sets an unlisted enumeration bit (2048) of the Configuration property and is used by SPList to determine whether to show or hide the Workflow Status column. |
InternalNameStatusField | The internal field name of the workflow association status column, note that this column is not created for you when this association is provisioned. |
RequireManagePermission | The permissions required to execute the workflow, the default value is; ViewListItems, EditListItems |
Configuration | This is a numeric [flag] type enumeration value controlling various aspects of the workflow association (see below). |
AssociationData | The URL encoded association data (see below). |
ContentTypeName | The name of the content type the workflow association is for, if the workflow association is with a ContentType. |
ContentTypeId | The list content type ID the workflow association is for, if the workflow association is with a ContentType ~ If you supply the content type using the ContentTypeName attribute (above) you can leave this attribute out. |
Configuration
This attribute value controls various aspects of the workflow association, such as;
- Starting characteristics
- Whether the workflow association has a status column
- Whether new instances can be created from this workflow association
The value is formed from a [Flag] type enumeration found in the Microsoft.SharePoint.Workflow namespace in the SPWorkflowAssociationCollection class.
[Flags] public enum Configuration { None = 0, AutoStartAdd = 1, AutoStartChange = 2, AutoStartColumnChange = 4, AllowManualStart = 8, HasStatusColumn = 16, LockItem = 32, Declarative = 64, NoNewWorkflows = 128, AllowMultipleWorkflows = 256, // unlisted in official Enumeration MarkedForDelete = 512, GloballyDisabled = 1024, StatusColumnShown = 2048, // unlisted in official Enumeration CompressInstanceData = 4096, SiteOverQuota = 8192, SiteWriteLocked = 16384, AllowAsyncManualStart = 32768, SkipContentTypePushDown = 65536 }
AssociationData
The AssociationData attribute contains the URL encoded association data for this workflow and should match that defined in the workflow Workflow.xoml.wfconfig.xml file, typically it is of the form;
<dfs:myFields xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:dms="http://schemas.microsoft.com/office/2009/documentManagement/types" xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:q="http://schemas.microsoft.com/office/infopath/2009/WSSList/queryFields" xmlns:d="http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields" xmlns:ma="http://schemas.microsoft.com/office/2009/metadata/properties/metaAttributes" xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <dfs:queryFields/> <dfs:dataFields> <d:SharePointListItem_RW/> </dfs:dataFields> </dfs:myFields>
InternalNameStatusField
This attribute contains the internal column name of the workflow association status column, normally when you create a Workflow Association using the UI, this column is created for you. This isn’t the case when using the WorkflowAssociation
element, you must create the column yourself.
You can use the code below to trawl the feature definition WorkflowAssociation
elements, and use the association attributes to create the status columns for you;
private const string WfAssocStatusField = @"<Field DisplayName=""{0}"" Type=""WorkflowStatus"" Required=""FALSE"" ID=""{1}"" SourceID=""{{080b36b1-d6c9-45ae-b69f-852ea16b6ce3}}"" StaticName=""{2}"" Name=""{2}"" ColName=""nvarchar8"" RowOrdinal=""0"" WorkflowStatusURL=""_layouts/WrkStat.aspx"" ReadOnly=""TRUE""> <CHOICES> <CHOICE>Starting</CHOICE> <CHOICE>Failed on Start</CHOICE> <CHOICE>In Progress</CHOICE> <CHOICE>Error Occurred</CHOICE> <CHOICE>Canceled</CHOICE> <CHOICE>Completed</CHOICE> <CHOICE>Failed on Start (retrying)</CHOICE> <CHOICE>Error Occurred (retrying)</CHOICE> <CHOICE /> <CHOICE /> <CHOICE /> <CHOICE /> <CHOICE /> <CHOICE /> <CHOICE /> <CHOICE>Canceled</CHOICE> <CHOICE>Approved</CHOICE> <CHOICE>Rejected</CHOICE> </CHOICES> </Field>"; private static void CreateWorkflowAssociationStatusColumns(SPFeatureReceiverProperties properties, SPWeb web) { if (properties == null) throw new ArgumentNullException("properties"); if (web == null) throw new ArgumentNullException("web"); // select the feature definition WorkflowAssociation elements var fdElements = properties.Definition.GetElementDefinitions(CultureInfo.InvariantCulture); var wfAssociations = fdElements.Cast<SPElementDefinition>() .Where(el => el.XmlDefinition.Name.Equals("WorkflowAssociation")) .Select(el => el.XmlDefinition.Attributes) .ToList(); foreach (var wfAssociation in wfAssociations) { var listUrl = web.ServerRelativeUrl + "/" + wfAssociation["ParentList"].Value; var fInternalName = wfAssociation["InternalNameStatusField"].Value; var list = web.GetList(listUrl); var fchk = list.Fields.Cast<SPField>() .FirstOrDefault(f => f.InternalName.Equals(fInternalName)); if (fchk != null) continue; // create the field var fDisplayName = wfAssociation["Name"].Value; var fid = Guid.NewGuid(); var fieldXml = string.Format(WfAssocStatusField, fInternalName, fid.ToString("B"), fInternalName); list.Fields.AddFieldAsXml(fieldXml, true, SPAddFieldOptions.AddFieldToDefaultView); // set the fields correct display name var updFld = list.Fields[fid]; updFld.Title = fDisplayName; updFld.Update(); } }
Extended Status Column Values
If your Workflow expresses additional extended status column values (found in the Workflow.xoml.wfconfig.xml file) as is the case in the code shown above, you should add them to the field definition of your workflow status column as additional <CHOICE /> values ~ the empty <CHOICE /> values are to fill the gap between the standard workflow status values and where (custom) extended status column values should start, see the SPWorkflowStatus enumeration and this article on MSDN for clarification.
One thought on “Creating SharePoint Declarative Workflow Associations using a Feature”