Saturday, 6 August 2011

Deploying reusable SharePoint 2010 workflow

In this post I will try to cover the procedure of deploying reusable workflow in SharePoint 2010 as a VisualStudio project. What I found most challenging is how to do this when the workflow has collect data task. For 2 days I've been fighting different problems, and finally got to some solution. This solution is described below, and hopefully will save you at least 2 days in struggle.

Scenario

I have a dev server, where a reusable workflow is designed. This workflow has a task to collect data from some user, and based on the input to perform some other actions. The workflow has to be imported to a project, that will provision a web site, custom lists, content types, application pages etc. Shortly - this workflow is a piece of a bigger picture and as such needs to be part of the deployment procedure.
So far, so good ... According to all posts and articles I've read, this should not be a problem - all I have to do is export the reusable workflow as WSP solution, import it in VisualStudio project (there is a nice project type called "Import Reusable Workflow" for SharePoint) and that is all. Well, not at all ...

Problems

Here are just few of problems I hit, at least the biggest I remember now. There were other problems as well, but most of them were a matter of proper configuration.

  • Forms are not found (note that these are InfoPath forms) - first of all, they are not deployed properly, actually they were not deployed at all. Then, they were not attached to the workflow definition.

  • Then collect data task could not complete, nor update its fields. Here I used the well-known try-fail development process till proper solution was found.


Enough talking, let's get started ...


Step 1 - Design the workflow

Well, there is nothing special here - open SharePoint designer, select to create reusable workflow, attach some event actions, one of which is collect data, save and publish the workflow. As a result, my workflow looks like this:

This workflow has only one step with few actions: log to history list, then collect data from user, extract the data and log it to history list again.
Now, save the workflow as template. Pay attention to 2 things here:

  • Workflow name should not contain spaces or any other "illegal" characters for class name. If there are such characters, when imported in VisualStudio, workflow name and its code-behind class as well, will not be considered as a valid project items (we cannot have spaces in class names).

  • Save and publish the workflow before creating the template, otherwise the WSP will be empty.


Once published, save the workflow as template:

The workflow template will be saved to Site Assets library as a WSP file. Download it from there and let's move on.


Step 2 - Create VisualStudio project

Select to create a new "Import Reusable Workflow" project VisualStudio 2010 and follow the project wizard. Once the workflow is imported, project tree will look like this:

There is one feature that will deploy the elements, a workflow definition with collect data task content type in it and 2 InfoPath forms (for initialization and collect data). Here I can do some cosmetic improvements like extracting the content type definition in a separate folder and renaming the feature, but have more important things to do now.


Step 3 - Fix InfoPath files

As I mentioned above in the problems area, InfoPath forms are not integrated properly in the project. They are attached as project files, but not deployed, and not integrated with the workflow. To deploy the forms, will create a Module that will attach them to the package. To make the forms work with the workflow, will have to register them in Central Administration.
Let's create the module first. Create a new folder "Forms" and add a Module there called "Forms" again. Then drag-drop all InfoPath forms into the module and that is all. Now my project tree looks like this:

I have extracted the content type to "ContentTypes" folder and renamed the feature in addition.
When creating new SharePoint item in VisualStudio it is automatically attached to some feature at its choice. In my case there is just one feature and this is why the Forms module is already attached to it. But since those forms have to be imported as administrator approved forms in Central Administration, will create a separate feature for them. Remove the forms module from "MyReusableWorklow" feature and create another feature "MyReusableWorklow Forms" where to attach it. The new feature will be on a site collection level as well.

To deploy the forms as administrator approved forms to Central Administration, open forms feature properties and type "Microsoft.Office.Workflow.Feature, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" for receiver assembly and "Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver" for receiver class:

SharePoint's workflow feature event receiver will take care for importing and activation of the InfoPath forms in Central Administration, and for removing them on feature deactivation. Still, have to provide the receiver with information where are forms to deploy and how to deploy them. This is done with the following properties in feature property bag:

Now all "xsn" forms in "Forms" folder will be registered globally.
Next is to tell the workflow where its forms are. This is done with "Instantiation_FormURN" and "Task0_FormURN" (considering that collect data task is the first one in the workflow) tags under "MetaData" node. Those 2 tags keep InfoPath forms IDs as they are registered in Central Administration. To get the IDs of the forms, open each form file in design mode, select File tab and open Form template properties. Then copy the form ID and add it in the proper tag data.

This is how the modified elements file look like:

Tip: you have 2 ways to understand which form is for initialization and which for task:

  • The task form has "data" as suffix in its name: "MyReusableWFData.xsn"

  • When you open the form in design mode, the task form will contain your collect data fields




Step 4 - Fix Collect Data task

Now forms are fixed, we can deploy the solution, attach and start the workflow, but when try to open the task, will get exception that page cannot be found. The reason for this exception is in collect content type task definition - for edit and display form it has something like this: "_layouts/MyReusableWFProject/My Reusable WF DataFT/". The quick fix is to use InfoPath workflow task form that ships with SharePoint installation: "_layouts/WrkTaskIP.aspx":



Step 5 - Testing

It is time to build and deploy the solution. Activate both features (if they are not activated). Select a list to attach the workflow to. Note - my workflow is designed as reusable for "Item" content type. This allows me to attach it to any list or content type that is instantiated from Item.
To be able to create workflow tasks from my collect data task content type, have to attach it to Tasks list:

When designing the workflow this is done by SharePoint Designer, but here has to be done manually.
Now attach the workflow to a list, and start it for a list item in it. Workflow starts successfully, it is "In Progress" and is waiting for collect data task to complete:

I can open the task enter some data in collect data fields and click on "Complete task" button:

This action normally should complete the task and the workflow, but once I open workflow info page it still shows "In Progress" and the task is not completed. Further checks show that task fields are not updated as well. To solve these issues, I have created a custom workflow InfoPath task page.


Step 6 - Custom Task Page

The easiest way to create custom InfoPath task page is to create an APSX page, that inherits from "WrkTaskIPPage" class. This is actually the code-behind class that uses "WrkTaskIP.aspx". WrkTaskIPPage class is part of "Microsoft.Office.Workflow.Pages" assembly, and this is the first one to reference in our solution.

To display InfoPath form within the ASPX page, the " XmlFormView" control is used. Actually, to make it easier I will copy the entire content from "WrkTaskIP.aspx" page and keep only the "Page" and "Assembly" tags. The "XmlFormView" control is located in "Microsoft.Office.InfoPath.Server" assembly, so will have to reference it in project as well.
Once the ASPX page is done, it is time to take a look at its code-behind class. The code, that completes a task, is located in close form method. This is the one I will overwrite in order to fix the issue with task completion. There are 2 most important modifications here. The first one is to fix form data to get the proper location of collect data hashtable:

This fixes the issue with updating task fields, but task still does not complete. This is why the second code snippet is used:

Well, there are few things that can be improved here - like getting the actual outcome status, and check if task approvers are more than one, but in my case this is not needed.
Next is to make collect data task to use the custom complete task page:

Now can publish and test it all again.
This time all seems fine - collect data task completes successfully, workflow completes as well and so my problem is solved.


The code for this post can be downloaded from here: click to download. The ZIP file contains the exported WSP as well.

1 comment:

  1. Cannot download file, Can you post it in a cleaner place (alots of adds & issues)

    ReplyDelete