Thursday, February 18, 2010

PS Project Workflow - Programmatic deployment of Workflow Association

One of the key features of MS Project Server (PS) is the ability to track its Project entity life-cycle with the workflow. The basic idea of this feature is to have several stages the project can go through. These stages can manage the accessibility of any particular Project Detail Page (PDP) for the project under the current stage; make its fields required or read-only; track Strategic Impact behavior for project business drivers.

In order to develop the Workflow for PS project it is necessary go through the following steps:

  1. construct the Sequence Workflow in MS Visual Studio Designer (check the http://msdn.microsoft.com/en-us/library/ee767686(office.14).aspx article);
  2. deploy workflow template, create a Workflow Association entity in PWA and Connect the particular PS Enterprise Project Type (EPT) to the newly created Workflow Association (check the http://msdn.microsoft.com/en-us/library/ee767699(office.14).aspx article).

Nevertheless, there is a way to automate workflow deployment process – deploy Workflow Association and Workflow-controlled EPT programmatically. In this post you can find the description of how to deploy a Workflow Association SharePoint feature Event Receiver.

It is possible to create a Workflow Association with help of custom code in FeatureActivated overridden SharePoint feature method. Here is the code sample:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    var site = (SPSite)properties.Feature.Parent;
    using (var web = site.OpenWeb())
    {
        var info = new
                    {
                        Name = "[Your_WF_Association_Name]",
                        TemplateName = "[Your_WF_Template_Name]",
                        TaskListTitle = "[Your_Task_list_Title]",
                        HistoryListTitle = "[Your_istory_list_Title]",
                    };

        var workflowTemplate = web.WorkflowTemplates
            .OfType()
            .First(wfTemplate => wfTemplate.Name == info.TemplateName);

        for (int i = 0; i < web.WorkflowAssociations.Count; i++)
            if (web.WorkflowAssociations[i].Name == info.Name)
                web.WorkflowAssociations.Remove(web.WorkflowAssociations[i]);

        SPList taskList = GetListByNameAndBaseType(
                              web,
                              info.TaskListTitle,
                              SPListTemplateType.Tasks)
                          ?? GetFirstListByNameAndType(
                                 web,
                                 SPListTemplateType.Tasks);

        SPList historyList = GetListByNameAndBaseType(
                              web,
                              info.HistoryListTitle,
                               SPListTemplateType.WorkflowHistory)
                          ?? GetFirstListByNameAndType(
                                 web,
                                 SPListTemplateType.WorkflowHistory);

        SPWorkflowAssociation wfAssociation = SPWorkflowAssociation.CreateWebAssociation(
        workflowTemplate,
        info.Name,
        taskList, 
        historyList);

        web.WorkflowAssociations.Add(wfAssociation);
    }
}

private SPList GetFirstListByNameAndType(
    SPWeb web,
    SPListTemplateType listTemplateType)
{
    return web.Lists
        .OfType()
        .FirstOrDefault(list =>
                        list.BaseTemplate == listTemplateType);
}

private SPList GetListByNameAndBaseType(
    SPWeb web,
    string taskListTitle,
    SPListTemplateType listTemplateType)
{
    return web.Lists
        .Cast()
        .FirstOrDefault(list =>
                        list.BaseTemplate == listTemplateType
                        && list.Title == taskListTitle);
}

In this code there is an object named "info" that contains the following properties:

  1. Name – the name of the Association to be created;
  2. TemplateName – should be taken from the Name attribute of Workflow XML declaration node;
  3. TaskListTitle – the SharePoint list name to be used by this workflow association ("Project Server Workflow Tasks" value is used by default);
  4. HistoryListTitle – the SharePoint list name to be used by this workflow association ("Project Server Workflow History" value is used by default);

IMPORTANT. The Workflow Association should be added to the WorkflowAssociations collection of the Root Web of site collection.

In the next post we will show how to programmatically create Workflow-controlled EPT.

2 comments: