Creating SharePoint Declarative Workflow Associations using a Feature


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
  • ContentType ~ Workflow is associated with a ContentType
  • Web ~ Workflow is associated with a site
  • List ~ Workflow is associated with a List
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.

Published by

Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

One thought on “Creating SharePoint Declarative Workflow Associations using a Feature

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.