There isn't an official fix to this as far as I'm aware.
The example approval workflow included in the Sharepoint SDK is pretty close to the OOTB version, so I'm trying to extend that rather than start from scratch.
One of the differences is that the example just seems to set the workflow's status i.e. it doesn't seem to set the moderation status of the file (this controls whether the list item is draft, pending, published etc) I've updated the SetWorkflowState method to make this additional change.
e.g.
itemStatus = SPModerationStatusType.Approved;However, this doesn't fix the update-restarts-the-workflow problem yet. To do this I disable event firing while the update is made. This makes use of the following simple class:
this.workflowProperties.Item.ModerationInformation.Status = itemStatus;
///
/// Utility class, used to avoid the race condition that
/// occurs when approving an item. The approval is considered
/// a change so the approval workflow kicks off again. This class
/// disables event firing to stop this.
///
#region Events Class
public class Events : SPItemEventReceiver
{
public void DisableEvents()
{
this.DisableEventFiring();
}
public void EnableEvents()
{
this.EnableEventFiring();
}
}
#endregion
The final code for SetWorkFlowState is as follows:
///
/// Sets the state (Canceled, Rejected, Approved) of the workflow. States are
/// set in the workflow template file under Metadata/ExtendedStatusValues
///
private void SetWorkflowState(object sender, EventArgs e)
{
SPModerationStatusType itemStatus;
//Is the workflow canceled?
if (this.isWorkflowCanceled)
{
this.workflowState = (int)SPWorkflowStatus.Max;
itemStatus = SPModerationStatusType.Draft;
}
//Is the workflow rejected?
else if (this.isWorkflowRejected)
{
this.workflowState = (int)SPWorkflowStatus.Max + 2;
itemStatus = SPModerationStatusType.Denied;
}
//The workflow is approved.
else
{
this.workflowState = (int)SPWorkflowStatus.Max + 1;
itemStatus = SPModerationStatusType.Approved;
}
if (this.initFormData["ApproveWhenComplete"].ToString() == "true")
{
Events oEvent = new Events();
oEvent.DisableEvents();
this.workflowProperties.Item.ModerationInformation.Status = itemStatus;
//Commit the changes (without triggering the workflow again!)
this.workflowProperties.Item.SystemUpdate();
oEvent.EnableEvents();
}
}
Now its a case of testing everything to spot any other differences between the example workflow and the OOTB version! Any comments/improvements/alternatives would be appeciated!