Introduction

This blog post will show you how to check if a certain workflow instance is completed in a SharePoint list. In this example, let’s assume that the default “Documents” document library in a team site is configured with a Collect Feedback workflow and that a new instance is started every time a new document is added to the library. The workflow is configured with 2 reviewers. My purpose is to start a custom workflow (associated with a custom workflow) on each document every time all reviewers have reviewed them, i.e., every time a Collect Feedback workflow instance is completed.

Example

The following example shows how the previously described scenario can be implemented. One first note is that when a workflow instance gets completed on a list item (thus changing the workflow status of the item), the item is not updated. This means that the obvious solution that would be to have an event handler running on the document library to detect the completion of the workflow for each document can’t be implemented. The following example uses an alternative approach that makes use of an event handler associated with the “Task” content type, that is used on workflow tasks lists. The event handler will run every time a task is updated and will get the task associated list item (the document) and start a custom workflow on the document when its Collect Feedback workflow instance gets completed. Let’s take a look at the code:

/// <summary>
/// Event handler associated with the "Task" content types.
/// </summary>
public class WorkflowCompletionCheckerEventReceiver : SPItemEventReceiver
{
//custom content type name
private const string CUSTOM_CONTENT_TYPE_NAME = 
"My Custom Content Type";
//task list workflow list id field
public const string TASK_LIST_WORKFLOW_LIST_ID_FIELD_NAME = 
"ows_WorkflowListId";
//task list workflow item id field
public const string TASK_LIST_WORKFLOW_ITEM_ID_FIELD_NAME = 
"ows_WorkflowItemId";
//collect feedback workflow id
public const string COLLECT_FEEDBACK_WORKFLOW_ID = 
"46c389a4-6e18-476c-aa17-289b0c79fb8f";
//custom workflow id
public const string CUSTOM_WORKFLOW_ID = 
"96be29b0-1be4-4b99-a703-656e5470962b";
/// <summary>
/// Event handler for item updated event.
/// </summary>
/// <param name="properties"></param>
public override void ItemUpdated(SPItemEventProperties properties)
{
//start a custom workflow on a list item when a 
//collect feedback workflow gets completed
StartCustomWorkflowOnTaskItemCompletion(properties);
}
/// <summary>
/// Checks if the "Collect Feedback" workflow 
/// instance on an item is completed. This method checkes a 
/// workflow task and verifies if they have been created by a 
/// "Collect Feedback" workflow and if the associated item is of 
/// my custom content type. If yes, it verifies if the workflow has 
/// been completed and if complete, starts a custom workflow 
/// on the associated item.
/// </summary>
/// <param name="properties"></param>
private void StartCustomWorkflowOnTaskItemCompletion
(SPItemEventProperties properties)
{
//get the task item
SPListItem listItem = properties.ListItem;
if (listItem != null)
{
//get associated item id and associated list id
//list id - ows_WorkflowListId field
object associatedWorkflowListId = 
listItem[TASK_LIST_WORKFLOW_LIST_ID_FIELD_NAME];
//item id - ows_WorkflowItemId field
object associatedWorkflowItemId = 
listItem[TASK_LIST_WORKFLOW_ITEM_ID_FIELD_NAME];
//get associated item
if (associatedWorkflowItemId != null 
&& associatedWorkflowListId != null)
{
//get task associated list item using 
//its list id and item id
SPListItem associatedListItem = listItem.Web.Lists
.GetList(new Guid(associatedWorkflowListId.ToString()), false)
.GetItemById(int.Parse(associatedWorkflowItemId.ToString()));
//check if item is of my custom content type
if (associatedListItem.ContentType.Name
.Equals(CUSTOM_CONTENT_TYPE_NAME))
{
//check if item has any workflow association
if (associatedListItem.Workflows.Count > 0)
{
//iterate workflow associations and lookup 
//the collect feedback association
foreach (SPWorkflow workflow in 
associatedListItem.Workflows)
{
Guid collectFeedbackWorkflowId = 
new Guid(COLLECT_FEEDBACK_WORKFLOW_ID);
//check if collect feedback workflow 
//instance is completed
if (workflow.ParentAssociation.BaseId 
== collectFeedbackWorkflowId
&& workflow.InternalState 
== SPWorkflowState.Completed)
{
//if collect feedback workflow instance is 
//completed, start custom workflow that is 
//associated with the item
StartContentTypeWorkflow(associatedListItem, 
CUSTOM_WORKFLOW_ID);
}
}
}
}
}
}
}
/// <summary>
/// Starts a workflow associated to a content type programmatically.
/// </summary>
/// <param name="properties"></param>
public static void StartContentTypeWorkflow(SPListItem listItem,
string workflowId)
{
//start specified workflow that is associated with the item
SPWorkflowManager manager = listItem.Web.Site.WorkflowManager;
//get list item content type
SPContentType contentType = listItem.ContentType;
//get list item workflow associations
SPWorkflowAssociationCollection associationCollection = 
contentType.WorkflowAssociations;
//iterate all item workflow associations
foreach (SPWorkflowAssociation association in associationCollection)
{
Guid workflowGuid = new Guid(workflowId);
//check if current workflow association matches 
//the specified workflow
if (association.BaseId == workflowGuid)
{
string data = association.AssociationData;
//start workflow
SPWorkflow wf = manager.StartWorkflow(listItem, association, 
data, false);
}
}
}
}

In the previous example, the following actions are being performed:

  • The ItemUpdated event in the event handler is being handled;
  • In the event handler, a reference to the task is obtained;
  • The associated list item is obtained using two columns of the task list item:
    • “ows_WorkflowListId” – holds the id of the list where the associated list item is created;
    • “ows_WorkflowItemId” – holds the id of the associated list item;
  • The content type of the associated list item is checked for a match to my custom content type;
  • If there is a match, the list item workflow associations are checked for a match with the Collect Feedback workflow;
  • If there is a match and the workflow instance is completed, the custom workflow is started.

Related Articles

To learn why your business should migrate to SharePoint Online and Office 365, click here and here.

If you want to convert your tenant’s root classic site into a modern SharePoint site, click here.

If you are a SharePoint administrator or a SharePoint developer who
wants to learn more about how to install a SharePoint farm in an automated way using PowerShell, I invite you to click here and here. The articles use AutoSPInstaller with a SharePoint 2016 farm but AutoSPInstaller support for SharePoint 2019 was already announced!

If you want to learn how to upgrade a SharePoint 2013 farm to SharePoint 2019, click here and here.

If you want to learn all the steps and precautions necessary to successfully keep your SharePoint farm updated and be ready to start your move to the cloud, click here.

If you learn how to greatly speed up your SharePoint farm update process to ensure your SharePoint farm keeps updated and you stay one step closer to start your move to the cloud, click here.

If you want to learn how to upgrade a SharePoint 2010 farm to SharePoint 2016, click here and here.

If you are new to SharePoint and Office 365 and want to learn all about it, take a look at these learning resources.

If you are work in a large organization who is using Office 365 or thinking to move to Office 365 and is considering between a single or multiple Office 365 tenants, I invite you to read this article.

If you or your customers are not ready to move entirely to the Cloud and Office 365, a hybrid scenario could be an interesting scenario and SharePoint 2019 RTM was recently announced with improved hybrid support! To learn all about SharePoint 2019 and all its features, click here.

If you want to know all about the latest SharePoint and Office 365 announcements from SharePoint Conference 2019, click here and here.

Happy SharePointing!

LEAVE A REPLY

Please enter your comment!
Please enter your name here