Programmatically setting up Approval workflow in MOSS

Out-of-box Approval works pretty well for simple content approval and that’s what exactly I wanted. If you attach an Approval workflow to a list from the UI you get number of options to set such as Approvers, People who needs to be notified and an important one if you want to update the approval status of a list item to Approved or Rejected is “Update the approval status (use this workflow to control content approval)” in “Post completion Workflow Activities”

As in most the real world situation I want to set this up during deployment process and not from the UI.

Following code snippet will set up your list with Moderation enabled:

SPList list = web.Lists["MyList"];
list.EnableModeration = true;
list.Update();

Following code snippet sets up the Approval work flow on the list

SPWorkflowTemplate baseTemplate = web.WorkflowTemplates.GetTemplateByName("Approval", CultureInfo.InvariantCulture);
SPWorkflowAssociation assoc = SPWorkflowAssociation.CreateListAssociation(baseTemplate, "Property Approval", web.Lists["Workflow Tasks"], web.Lists["Workflow History"]);
assoc.AllowManual = true;
assoc.AutoStartCreate = true;
string data = GetApprovalWorkFlowData();
assoc.AssociationData = data;
list.AddWorkflowAssociation(assoc);

In Above code snippet AssociationData property of SPWorkflowAssociation can be used to set things like Approvers, People who needs to be notified and “Post completion Workflow Activities” for content approval

AssociationData is in XML format and looks like:

<my:myFields xml:lang="en-us" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD">
  <my:Reviewers>
    <my:Person>
      <my:DisplayName>Ketul Patel</my:DisplayName>
      <my:AccountId>domain\kpatel</my:AccountId>
      <my:AccountType>User</my:AccountType>
    </my:Person>
  </my:Reviewers>
  <my:CC>
    <my:Person>
      <my:DisplayName>Joe Doe</my:DisplayName>
      <my:AccountId>domain\jdoe</my:AccountId>
      <my:AccountType>User</my:AccountType>
    </my:Person>
  </my:CC>
  <my:DueDate xsi:nil="true"></my:DueDate>
  <my:Description>A new Property has been added to the system and requires approval.</my:Description>
  <my:Title></my:Title>
  <my:DefaultTaskType>1</my:DefaultTaskType>
  <my:CreateTasksInSerial>true</my:CreateTasksInSerial>
  <my:AllowDelegation>true</my:AllowDelegation>
  <my:AllowChangeRequests>true</my:AllowChangeRequests>
  <my:StopOnAnyReject xsi:nil="true"></my:StopOnAnyReject>
  <my:WantedTasks xsi:nil="true"></my:WantedTasks>
  <my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>
  <my:MetadataSuccessField></my:MetadataSuccessField>
  <my:MetadataSuccessValue></my:MetadataSuccessValue>
  <my:ApproveWhenComplete>true</my:ApproveWhenComplete>
  <my:TimePerTaskVal xsi:nil="true"></my:TimePerTaskVal>
  <my:TimePerTaskType xsi:nil="true"></my:TimePerTaskType>
  <my:Voting>false</my:Voting>
  <my:MetadataTriggerField></my:MetadataTriggerField>
  <my:MetadataTriggerValue></my:MetadataTriggerValue>
  <my:InitLock>false</my:InitLock>
  <my:MetadataStop>false</my:MetadataStop>
  <my:ItemChangeStop>false</my:ItemChangeStop>
  <my:GroupTasks>false</my:GroupTasks>
</my:myFields>

Various elements in the above XML are pretty self explaining and roughly maps to relevant tick box or textbox on the “Customize Workflow” Page (CstWrkflIP.aspx)

<my:ApproveWhenComplete>true</my:ApproveWhenComplete>

Is the element to use to set the “Update the approval status (use this workflow to control content approval)” checkbox

Multiple Event Driven Activities in a Single Workflow State

I was working on developing a Custom State Machine Workflow in MOSS and had following scenario in a particular state of the workflow:

Create a Task –> Wait for Task to Be Created –> Send Email Alerts –> Wait for the User to Take Action on the Task.

To create a state activity with above scenario, I created a state activity that looked like:

Workflow Event Driven Activity in State Acitity
Figure 1

StateInitialization Activity contains CreateTask Activity

Workflow TaskCreate Activity
Figure 2

EventDriven activity contained following:

Workflow Event Driven Activity in State Acitity
Figure 3

Well the problem is “Only the first child activity of ‘EventDrivenActivity’ can implement the ‘System.Workflow.Activities.IEventActivity’ interface”

The solution is you can have State Activity within a state activity:

Workflow Event Driven Activity in State Acitity

Figure 4

First Child Activity’s StateInitialization contains CreateTaskActivity and EventDriven activity of the First Child contains OnTaskCreated Activity and SetState Activity to Set the state to Second Child (Child2)

Figure 5

Second Child’s EventDriven looks like:

Now you can have two Event Driven Activities in a single state!

Finally make sure the Correlation token’s Owner Acitity is the parentState Activity and not the Child State activity

Download the Sample Code here.

MOSS SendEmailActivity and Correlation Token

Recently I had to use SendEmail Activity to send E-mail alerts from a state machine workflow; SendEmail Activity was within a state activity and I was setting correlation token of SendEmail Activity to the token of state activity, unfortunately it was giving an error that sort of indicated that correlation token is not correct.

Somewhere hidden on MSDN forum I found that SendEmail actually needs Correlation token of the Workflow and not the state activity containing the SendEmail Activity

SendEmailActivity's Correlation Token