Thursday, October 27, 2011

Invoke SharePoint Workflow Programmatically

There's some restricted SharePoint sites and content in your SharePoint environment, and users can only interact with secure SharePoint resources through custom webpart(s). A typical example is performance review system. For simplicity let's say each department has a site or subsite to store the review data. You create a SPList for each year's review like PerformanceReview-2011, PerformanceReview-2012, etc. Only the management team members of a department can have direct access to the department performance review site. Every employee can input the data and respond manager's question/review through a custom webpart. Usually RunWithElevatedPrivileges is used inside the WebPart to get through the permission issue for SPList content update as code below:
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite site = new SPSite(siteID))
        {
            using (SPWeb web = site.OpenWeb(webID))
            {
                SPList list = web.Lists.TryGetList(listName);
                //Update List Item ...
            }
        }
    });
A workflow associated with the List is supposed to automatically kick off doing some extra stuff such as assigning task to corresponding managers. The problem is that the execution context of above code is system account, and updating List Item with System account won't trigger the workflow. So you have to start the workflow manually inside the WebPart:
    // Start a workflow manually since system account won't trigger workflow automatically
    private static void StartWorkflow(SPListItem listItem, string workflowName)
    {
        try
        {
            var listWorkflowAssociations = listItem.ParentList.WorkflowAssociations;
            foreach (SPWorkflowAssociation wfAssoication in listWorkflowAssociations)
            {
                if (wfAssoication.Name == workflowName)
                {
                    listItem.Web.Site.WorkflowManager.
                      StartWorkflow(listItem, wfAssoication, wfAssoication.AssociationData, true);
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            // Exception handling
        }
    }
Bear in mind that the Associator, Initiator and Current User inside Workflow context all are system account with such implementation. You have to use People field defined in List Item to reference the original user.