Saturday, July 10, 2010

SharePoint 2007: State Machine Workflows




Scenario: I have a SharePoint list ‘Knowledge Links’ which is open to all users. I wish to create a simple approval workflow wherein whenever a new ‘Knowledge Link’ is submitted by any user, a task would be created and assigned to a group ‘KT_Approvers’.

An Admin would review the link submitted. If he is satisfied with the link provided, he would mark the link as 100% complete by editing the task created for the same link.
Once the task is marked as 100% complete, the workflow will change the Status of the ‘Knowledge Link’ item as ‘Approved’ and the link can then be used in various views throughout the site. If the task is partially complete, the workflow will set the status to ‘Approval in Progress’.



1. Create a WSP Solution. Make sure you select ‘.NET Framework 3.0’



2. Add a ‘State Machine Workflow with Feature’ item to the solution.





     Note that Workflow features can be defined only at the site collection (i.e. Scope = Site)




3. The workflow solution requires the following three DLLs as reference which may not be present by default..


4. Add the three workflow DLLs to be required by the solution if they are not present by default.


   a. These DLLS can be found at the following location

   C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\

  b. To be able to refer to these DLLs , the solution should be using the .NET framework 3.0 or above. In other cases, you may find the following error in the workflow designer file.



 
c. The error can be removed by simply editing the csproj file as follows.

Close the Visual Studio solution currently in use and then Open the .csproj file present in the solution folder (shown below) in Notepad.



Find the following line.

 
 
Replace the value inside the   <ProjectTypeGuids> tag with the following line.

{F8810EC1-6754-47FC-A15F-DFABD2E3FA90};{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}

and re-open the solution in Visual Studio.


5. Now, add a new feature (a simple feature with receiver) which will associate this workflow to a list. Also called as Stapling feature. Stapling feature can be kept at the scope Web if required.

6. Remove the following lines from the elements.xml file in the workflow feature.

 
 
 
 
7. Enter the following code in the Feature Activated event of the Stapling feature.



public override void FeatureActivated(SPFeatureReceiverProperties properties)

{

SPList myList = null; // List to associate workflow to

string myListName = null; // My list name

SPList historyList = null; // Workflow history list

SPList taskList = null; // Workflow tasks list

string workflowTemplateGuid = null; // Workflow template Guid

SPWorkflowTemplate workflowTemplate = null; // Workflow template

SPWorkflowAssociation workflowAssociation = null; // Workflow association

string workflowAssocName = null; // Workflow association name

SPWeb spWeb = SPContext.Current.Web;

myListName = "Knowledge Links";

workflowAssocName = "KTStateMachine";

myList = spWeb.Lists[myListName];

workflowTemplate = spWeb.WorkflowTemplates.GetTemplateByName("KTStateMachine", System.Globalization.CultureInfo.CurrentCulture);

try

{

       historyList = spWeb.Lists["KT History"];

}

catch (ArgumentException exc)

{

// Create workflow history list

Guid listGuid = spWeb.Lists.Add("KT History", "", SPListTemplateType.WorkflowHistory);

historyList = spWeb.Lists[listGuid];

historyList.Hidden = true;

historyList.Update();

}

try

{

taskList = spWeb.Lists["Workflow Tasks"];

}

catch (ArgumentException exc)

{

// Create workflow tasks list

Guid listGuid = spWeb.Lists.Add("Workflow Tasks", "", SPListTemplateType.Tasks);

taskList = spWeb.Lists[listGuid];

taskList.Hidden = true;

taskList.Update();

}


// Allow unsafe updates on web

spWeb.AllowUnsafeUpdates = true;

try

{

// Create workflow association



if (null != workflowTemplate)

workflowAssociation = SPWorkflowAssociation.CreateListAssociation(workflowTemplate, workflowAssocName, taskList, historyList);



// Set workflow parameters

workflowAssociation.AllowManual = false;

workflowAssociation.AutoStartCreate = true;

workflowAssociation.AutoStartChange = false;



// Add workflow association to my list

if (null != myList)

{



SPWorkflowAssociation oldAssociation = myList.WorkflowAssociations.GetAssociationByName(workflowAssocName, System.Globalization.CultureInfo.CurrentCulture);



if (null != oldAssociation)

{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

myList.RemoveWorkflowAssociation(oldAssociation);

});

}

myList.AddWorkflowAssociation(workflowAssociation);

}



// Enable workflow

workflowAssociation.Enabled = true;



}

finally

{

spWeb.AllowUnsafeUpdates = false;

}



}



Verify you made the following changes

a. Enter the correct list name on which the workflow is going to be associated

b. Enter the workflow association name. You can find it in the elements.xml file of the Workflow feature



8. Go to the designer of the workflow created.

9. Rename the event driven activity created by default to ‘InitialStateActivity’

10. Double click on the activity to see the activity design view as shown

11. Rename the first event created by default to onKnowledgeLinkSubmitted


12. In the properties window, besides ‘Invoked’, define a new function ‘onLinkSubmitted_Invoked’ by simply typing the name there and press enter. An event handler will automatically be created. This is the event where you can initialize any class variables you might have created with the properties of the item which triggered this workflow. (E.g. title).

13. Add the code given below in this event.

private void onLinkSubmitted_Invoked(object sender, ExternalDataEventArgs e)

{

title = workflowProperties.Item["Title"].ToString();

}



14. Add a Create Task activity


 
 
15. Rename it to createApprovalTask


16. Enter a new correlation token ‘taskToken’. Set its owner activity name to ‘KTStateMachine’

17. Define an event ‘createApprovalTask_Invoked’ for method invoking

18. Bind the Taskid property to a new member (property) by clicking at the button given alongside. Select the options and enter values as shown below later.

19. Similarly, bind the TaskProperties to a new member (field).
 
 
 
 


20. Enter the following code in ‘createApprovalTask_Invoked’




private void createApprovalTask_Invoked(object sender, EventArgs e)

{

TaskId = Guid.NewGuid();



TaskProperties.TaskType = 1;

TaskProperties.Title = title;

TaskProperties.StartDate = DateTime.Today;

TaskProperties.PercentComplete = 0.0f;

TaskProperties.AssignedTo = "KT_Approvers";

}



21. Go to the designer view and click on the ‘KTStateMachine’ link in the top navigation.

 
 


22. Add four more states as shown below and rename them appropriately.



23. Now double click the Initial state Activity and add a Set State activity as shown


 
 
 
24. The workflow should look like this now from the main design view
 
 
 
25. In the State KTLinkTaskFormed, add an activity onKTLinkTaskFormed.


26. Inside this activity, add an onTaskChanged box and set its properties as given below.



27. Bind the AfterProperties to a new property as shown below

 
 
 
Do exactly the same for Before Properties.




28. Set its correlation token to taskToken and Owner Activity Name to KTStateMachine

29. Define the Invoked event ‘onLinkSubmitted_Invoked’

30. Bind the Task ID to the existing member tasked created before

31. The properties should like these

 
 
 
 
32. Create a IfElse Activity block

33. In the properties of the IfElseBranch Activity , set the properties as shown below


a. The condition isApproved will create a function in code which returns true if that condition is satisfied

b. Do the same for the other IfElseBranch Activity.

 
 
 
 
 
34. Add the following code inside isApproved & isRejected function


private void isApproved(object sender, ConditionalEventArgs e)

{

if(onTaskChanged_AfterProperties.PercentComplete == 1.0f)

e.Result = true;

}



private void isRejected(object sender, ConditionalEventArgs e)

{

e.Result = true;

}

35. Under each IfElseBranch activity, add a Set State Activity which will send the workflow to the appropriate state as shown below.

 
 


36. Go to the main workflow design view. You should see it as show below.







37. Add two state initiation activities, one each inside KTLinkApproved and KTLinkRejected.


38. Rename them as stateInitializationApproved and stateInitializationRejected 


 


39. Go inside ‘stateInitializationApproved’ activity


a. Add an ‘Update Task’ activity and set its properties as shown

 
 

b. Note: The TaskId property here should be bind to the existing member TaskId.


The TaskProperties property here should be bind to a new property ‘updateTaskonApproved_TaskProperties1’ as shown in the figure below.

 



 c. Also add a Set State event taking the state to ‘KTLinkReviewComplete’ state
 
 
 


40. Go inside ‘stateInitializationRejected’ activity and repeat the steps

 


41. Right Click the ‘KTLinkReviewComplete’ state and set it as completed state.


42. Finally the workflow should look like this.

 
 


43. Copy the following code in the update task events generated in the code file


private void updateTaskAfterApproved(object sender, EventArgs e)

{

workflowProperties.Item["Status"] = "Approved";

workflowProperties.Item.Update();

}



private void updateTaskAfterRejected(object sender, EventArgs e)

{

workflowProperties.Item["Status"] = "Approval In Progress";

workflowProperties.Item.Update();



}

44. Build and deploy the solution.

45. Go to the Site collection features and activate the ‘KTStateMachine’ feature.

 
 
 
 46. Go to the Site features and activate the ‘StaplingFeature’ .




47. Go to the ‘Knowledge Links’ list in your site.


48. Create a new item as shown below.



 
 
 
49. On creation, we can see the workflow being ‘In Progress’.
 
 
 
50. You will see a workflow task item being created in the ‘Workflow Tasks’ list as shown below.
 
 
 
51. Edit the item, and make its percent complete to 100%.
 
 
 
52. Now go back to the ‘Knowledge Links’ list. The item will have its ‘Status’ column marked as ‘Approved’
 
 
53. Note that if the task is changed but not marked as complete, the item will have the ‘Status’ column populated as ‘Approval in Progress’
 
 
 
There are many many more things to explore in SharePoint State Machine workflows. Will update the blog soon......
 
 





free counters

6 comments:

  1. great rahul and explain it so well and step by step really great

    ReplyDelete
  2. Great man, keep up the good work.

    ReplyDelete
  3. Good, how do I assign the task to multiple reviewers. I could not get an answer to this anywhere

    ReplyDelete
  4. Glad you liked the post. :)

    One way of assigning the task to multiple users would be creating a group of usersand assigning the task to it.

    In my example, I assigned the task to a group 'KT_Approvers'

    See the line
    TaskProperties.AssignedTo = "KT_Approvers";

    I believe you are trying to assign the task to many individual users at the same time, which should also be possible without creating a group..

    Will check on it and let you know..

    ReplyDelete
  5. Even if i approve or reject Task outcome field value is always empty. Any idea why? Thanks

    chak

    ReplyDelete