09 tháng 7, 2010

SharePoint workflow API

In this post I'd like to write down several important notes on working with workflow API in SharePoint.

Snippet code to assign an workflow template to a List or Content Type


Because the SharePoint feature schema doesn't support in assigning a workflow template to a specific List or Content Type, we must do it with code. A convenient place to do it is in Feature Activated event. Before looking at the code, you could check out Workflow Object Model Overview in SharePoint Foundation for some basic related-workflow classes. The snippet code below is what you could use to assign (associate) workflow template to List or Content Type


using System.Xml.Linq; // Include this namespace to use LINQ to XML

// ....

// When creating a workflow for list or content type, it needs two other list to operate: a list for tasks and a list for history
spWeb.Lists.Add("Workflow Tasks", string.Empty, SPListTemplateType.Tasks);
spWeb.Lists.Add("Workflow History Tasks",string.Empty, SPListTemplateType.WorkflowHistory);

// Retrieve Approval workflow template with it Guid defined in ReviewWorkflows feature
var approvalWFTemplate = spWeb.WorkflowTemplates[new Guid("C6964BFF-BF8D-41ac-AD5E-B61EC111731C")]; /* This guid is Approval Workflow , you can find it in ReviewWorkflows folder in Features */
var wfAssociation = SPWorkflowAssociation.CreateListAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
// If you want to assign workflow to content type in list, call CreateListContentTypeAssociation()
// var wfAssociation = SPWorkflowAssociation.CreateListContentTypeAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
wfAssociation.AllowManual = false;

XNamespace MyNamespace = @"http://schemas.microsoft.com/office/infopath/2003/myXSD";
XNamespace XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";

// The AssociationData property represents for the settings that user see in 2nd screen when creating workflow in SharePoint UI
var associationDataXml = XElement.Parse(wfAssociation.AssociationData);
associationDataXml.SetElementValue(MyNamespace+"CreateTasksInSerial", false); // Run workflow in parallel
associationDataXml.SetElementValue(MyNamespace + "Description", "Please review this task");
// Continue to modify appropriate xml elements for desired settings. See following paragraph to know elements and they are mapped to which settings on UI

// The element is little tricky
// It is by default
// If we want to set its value to true, remove the attribute first
var elementNameWithNS = MyNamespace + "StopOnAnyReject";
associationDataXml.Element(elementNameWithNS).Attribute(XsiNamespace + "nil").Remove();
associationDataXml.SetElementValue(elementNameWithNS, true);

// Reviewers is Approvers in UI
associationDataXml.Element(MyNamespace + "Reviewers").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName", "Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// CC in UI
associationDataXml.Element(MyNamespace + "CC").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName","Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// Update modifed xml segment back to WorkflowAssociation object
wfAssociation.AssociationData = associationDataXml.ToString();
var list = spWeb.Lists["Unapproved Document Library"];
list.AddWorkflowAssociation(wfAssociation);
// If you want to assign workflow template to content type in list, run below code
// list.ContentTypes["Custom ContentType"].AddWorkflowAssociation(wfAssociation);


The main idea here is that I modified the XML segment in AssociationData property which represents for settings in workflow association form. So how the XML segment in AssociationData looks like ? Here you're :

<my:myFields xml:lang="en-us" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD">

<my:Reviewers>
<my:Person>
<my:DisplayName>Managers</my:DisplayName>
<my:AccountId>Managers</my:AccountId>
<my:AccountType>SharePointGroup</my:AccountType>
</my:Person>
</my:Reviewers>

<my:CC></my:CC>
<my:DueDate xsi:nil="true"></my:DueDate>
<my:Description xsi:nil="true"></my:Description>
<my:Title></my:Title>
<my:DefaultTaskType>1</my:DefaultTaskType>
<my:CreateTasksInSerial>true</my:CreateTasksInSerial>
<my:AllowDelegation>false</my:AllowDelegation>
<my:AllowChangeRequests>true</my:AllowChangeRequests>
<my:StopOnAnyReject xsi:nil="true"></my:StopOnAnyReject>
<my:WantedTasks xsi:nil="true"></my:WantedTasks>
<my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>
<my:MetadataSuccessField></my:MetadataSuccessField>
<my:MetadataSuccessValue></my:MetadataSuccessValue>
<my:ApproveWhenComplete>false</my:ApproveWhenComplete>
<my:TimePerTaskVal xsi:nil="true"></my:TimePerTaskVal>
<my:TimePerTaskType xsi:nil="true"></my:TimePerTaskType>
<my:Voting>false</my:Voting>
<my:MetadataTriggerField></my:MetadataTriggerField>
<my:MetadataTriggerValue></my:MetadataTriggerValue>
<my:InitLock>true</my:InitLock>
<my:MetadataStop>false</my:MetadataStop>
<my:ItemChangeStop>false</my:ItemChangeStop>
<my:GroupTasks>true</my:GroupTasks>
</my:myFields>


Looking that XML, we have no idea which XML element goes with which setting. I figured out some of them. In below pictures, I point out settings and which XML is mapped to











Programming starting a workflow


Starting a workflow is not hard, just call StartWorkflow() method with appropriate parameters. But the third parameter is pretty tricky because the MSDN doesn't give any document for it. After soul searching, I found out this actually is the XML in AssociationData property. Here the sample code

// Get workflow association of the list
SPWorkflowAssociation approvalAssociation = null;
var requestDetailList = spWeb.Lists["Unapproved Document Library"];
foreach (SPWorkflowAssociation wfAssociation in requestDetailList.WorkflowAssociations)
{
if (wfAssociation.Name == "Sample Approval Workflow")
{
approvalAssociation = wfAssociation;
break;
}
}

if (approvalAssociation != null)
{
spSite.WorkflowManager.StartWorkflow(spWeb.Lists["Unapproved Document Library"].Items[0], approvalAssociation, approvalAssociation.AssociationData /* We can change AssociationData as our needs before giving to StartWorkflow() */ );
}


Just a few notes, hope it help