Thursday, July 26, 2007

Event Driven WF & WSS

So that technical post is finally here.

I've seen alot of posts recently about sharepoint and sequintial workflows, but precious little concerning sharepoint and event driven state machine workflows. But my current application requires a state machine workflow, so I'll share some of my experiances.

Let's use an example to illustrate. I'm developing a leave request application. After someone submits a request for leave, it must be approved by their line manager and by their current project manager and finally by the HR manager.

Any one of the approvers can refer the leave application back to the orginiator with a request to change the leave application in some way. If the originator resubmits, the workflow process must go throug the approval once again.
At any time before approval, the leave originator may cancel the request for leave.

Sound complicate? It's not. I'm not going to address the front end needs, you can use either ASPX or infopath and there are plenty of posts out there to show you how to hook up a front end to sharepoint. I'm just going to play around with sharepoint lists to provide a front end.

So, how do we approach this? Well, firstly, ensure that you've got WSS, VS 2005, WF Extensions for VS 2005 & WSS Extensions for VS 2005 installed on your dev machine (it just runs smoother that way).
Open up VS 2005. Create a new project of type State Machine Workflow Library as found under the sharepoint tab.
Call your solution LeaveApplication and your state machine library LeaveStateMachineFlow. Click OK.

You should now have a project with Workflow1.cs, feature.xml, workflow.xml & Install.bat. Rename Workflow1 to LeaveWorkflow.cs.

Our workflow has 6 states.

1. Initial State.
2. Applicant Editing.
3. Project Manager Approval.
4. Line Manager Approval.
5. HR Manager Approval.
6. Finalize.

You don't need the finalize state, but let's complete the workflow. We're going to start by developing only the initial state and the applicant editing state. So, let's get to work (no pun intended :) ).
Open up the LeaveWorkflow.cs file. You'll note that an initial state activity has already been added along with an event driven activity. We'll just change the names rather than adding new one. So change the name of the state activity to InitializeLeaveState. Open up the eventDrivenActivity1 by clicking on it. You should see something like this:

Notice the red exclamation marks. Those are errors that need be fixed before build will succeed.

If you click on the upper most exclamation mark, you'll see an error saying that the state is invalid. This is because we have changed the state name. Click on the error and the properites tab will take you to the correct entry to fix the error. Set the InitialStateName of LeaveWorkflow to InitializeLeaveState.

The exclamation mark has two errors. the correlation token has no valid owner activity and the workflow properties have no valid name. This is because we changed the workflow name. Set the correlation token owner activity to LeaveWorkflow and the workflow properites name to LeaveWorkflow.
The exclamation marks should dissapear and you should be able to build the solution.

Our next step is creating the six states. This is really simple. In your toolbox, there should be a state activity. Drag it onto the design canvas. Rename the state activities to ApplicantEditing, PMApproval, LineManagerApproval, HRApproval and LeaveFinalize. Right click on the finalize activity and mark it as the completed state. You should have something like this:

We'll ignore the other states for now and focus on creating the leave application. So, open up the eventDrivenActivity1 again. On your toolbox, check if you have the CreateTask activity. If you don't, right click on the toolbox, select Choose Items and select the items from the microsoft.sharepoint.WorkflowActions namespace.

Ok, now that the admin is done, drag the CreateTask activity from the toolbox to below your onWorkflowActivated1 event (this event is required by all sharepoint workflows). Rename CreateTask1 to createApplicantTask. Set the correlation token to ApplicantTaskToken and the owner activity as the workflow (so that you can have the task spread over mulitple states). Set the task ID to be a new field named applicantTaskID (Activity=LeaveWorkflow, Path=applicantTaskID) and the task properties to be a new field named createApplicantTask_TaskProperties1 (Activity=LeaveWorkflow, Path=createApplicantTask_TaskProperties1). So far so good. Now we come to the code. In your LeaveWorkflow.cs file, adding the following methods:

private void createApplicantTask_MethodInvoking(object sender, EventArgs e)
applicantTaskID = Guid.NewGuid();
createApplicantTask_TaskProperties1.AssignedTo = this.workflowProperties.Originator;
createApplicantTask_TaskProperties1.Description = "Complete your leave application";
createApplicantTask_TaskProperties1.Title = "Leave Application";
private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)

Link these methods to the handlers of task creation (creatApplicantTask) and the workflow activated (onWorkflowActivated1) respectivly. This will create a task for the leave applicant to complete his leave application after creation. Let's see it in action shall we? First we need to edit the workflow.xml and feature.xml files so that the feature for the workflow is correctly installed. By the same token, you must edit the Install.bat file. Your files should look something like this: (remember, your assembly needs a strong name) (Don't forget snippets for the workflow.xml and the feature.xml, they should already be installed):


Description="This workflow handles leave approval"
CodeBesideAssembly="LeaveStateMachineFlow, Version=, Culture=neutral, PublicKeyToken=1851b6189ce8aae4">


Description="This feature is a workflow that approves leave"


:: Before running this file, sign the assembly in Project properties
:: To customize this file, find and replace
:: a) "MyFeature" with your own feature names
:: b) "feature.xml" with the name of your feature.xml file
:: c) "workflow.xml" with the name of your workflow.xml file
:: d) "http://localhost" with the name of the site you wish to publish to
echo Copying the feature...
rd /s /q "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\LeaveStateMachineFlow"
mkdir "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\LeaveStateMachineFlow"
copy /Y feature.xml "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\LeaveStateMachineFlow\"
copy /Y workflow.xml "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\LeaveStateMachineFlow\"
xcopy /s /Y *.aspx "%programfiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"
echo Adding assemblies to the GAC...
"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf LeaveStateMachineFlow
"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\LeaveStateMachineFlow.dll
:: Note: 64-bit alternative to lines above; uncomment these to install on a 64-bit machine
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf LeaveStateMachineFlow
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\LeaveStateMachineFlow.dll

echo Activating the feature...
pushd %programfiles%\common files\microsoft shared\web server extensions\12\bin
::Note: Uncomment these lines if you've modified your deployment xml files or IP forms
::stsadm -o deactivatefeature -filename LeaveStateMachineFlow\feature.xml -url http://localhost
::stsadm -o uninstallfeature -filename LeaveStateMachineFlow\feature.xml
stsadm -o installfeature -filename LeaveStateMachineFlow\feature.xml -force
stsadm -o activatefeature -filename LeaveStateMachineFlow\feature.xml -url http://localhost

echo Doing an iisreset...

Build the project and run the install.bat file.

Now open up your sharepoint site (the same one you installed the feature to). Create a list with a title, from date and to date (we'll add more stuff later). Go to the list settings, go to workflows, add the leave workflow and set it to start when a new item is added. Add a new item to the list and voila a task is added to the initiator's task list. (It's a good idea to create a new task list for the workflow, just to keep things neat).

To see your tasks and so forth, click on the down arrow of the item, click on workflows, click on your workflow and the task is displayed.

Next post: How to move from one state to the next (or how to go to the approvers and back).

No comments: