Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / SharePoint / SharePoint2013

Get List of all Suspended/Terminated Workflows using JavaScript - SharePoint Online

0.00/5 (No votes)
27 Jan 2020CPOL 7.1K   111  
How to get a list of all suspended/terminated workflows using JavaScript - SharePoint online

Introduction

This tip will help developers to find the list of all suspended/errored list/site workflows (Nintex/Designer) in a single page, also list down the inventory of workflows with basic details. I have tested the below solution in SharePoint, but I feel that this should work in SharePoint 2013/2016/2019.

Background

Generally, users need to go to each list item to identify the suspended workflows, it is time consuming and also difficult to identify issues.

Using the Code

I am using SharePoint JSOM and Angularjs with the help of content editor webpart to display the data in table format.

Image 1

Steps

  1. Create an empty HTML page and add the below HTML.
  2. Update js references:
HTML
<html>
<script type="text/javascript" 
src="/_layouts/15/sp.runtime.js"></script>
<script type="text/javascript" 
src="/_layouts/15/sp.js"></script>
<script src="/_layouts/15/sp.workflowservices.js"></script>
<script type="text/javascript" 
src="SiteURL/SiteAssets/JS/angular1.7.5.min.js"></script>
<script type="text/javascript" 
src="SiteURL/SiteAssets/JS/angular-route.min.js"></script>
    <script type="text/javascript" 
    src="SiteURL/SiteAssets/JS/jquery-3.2.1.min.js"></script>
<script type="text/javascript" 
src="SiteURL/SiteAssets/JS/moment.js"></script>
<style>
.ms-webpartPage-root{
    border-spacing:2px;
}
</style>
<body >
<div ng-app="WFApp" ng-controller="myCtrl">
<h3 style="    
color: blue;">List Workflow Errored in last {{QueryPastDays}} days</h3>
<div id="ProgressDiv">Please wait...</div>
<div style="padding:5px">Filter by Status: 
<input ng-model="SearchStatus" 
style=""></div>
<table style="margin:10px" 
border="1" class="table table-bordered">
<tr>
    <td style="color:black;font-weight:bold;
    padding:5px;padding:5px">List Name</td>
    <td style="color:black;font-weight:bold;
    padding:5px">WF Name</td>    
    <td style="color:black;font-weight:bold;
    padding:5px">Item ID</td>
    <td style="color:black;font-weight:bold;
    padding:5px">Title</td>
    <td style="color:black;font-weight:bold;
    padding:5px">Status Code</td>
    <td style="color:black;font-weight:bold;
    padding:5px">Status</td>
     <td style="color:black;font-weight:bold;
     padding:5px">Item Modified</td>
    <td style="color:black;font-weight:bold;
    padding:5px">Workflow Link</td>
    <td style="color:black;font-weight:bold;
    padding:5px">Workflow Instance Link</td>
    <td style="color:black;font-weight:bold">ErrorDetails</td>
  <tr ng-repeat="x in WFWithListNames | orderBy:'-Modified' | 
   filter:{ StatusName:SearchStatus || undefined}">
    <td>{{ x.ListName}}</td>
     <td>{{ x.WFName}}</td>    
    <td><a href="{{x.ItemLink}}" 
    target="_blank">{{ x.ItemId}}</a></td>
     <td>{{ x.Title}}</td> 
    <td>{{x.Status}}</td>   
    <td><span ng-show="x.StatusName=='Suspended'" 
    style="color:red">{{x.StatusName}}</span>
        <span ng-show="x.StatusName!='Suspended'">
        {{x.StatusName}}</span></td>
    <td>{{x.Modified | date :  "dd-MMM-y h:mm a" }}</td>    
    <td><a href="{{x.WorkflowLink}}" 
    target="_blank">Click here</a></td>
    <td><a href="{{x.WorkflowInstanceLink}}" 
    target="_blank">{{x.InstanceID}}</a></td>
    <td>{{x.ErrorDetails}}</td>
  </tr>
</table>
<h3 style="    color: blue;">Site Workflow Errored as of Today 
               <a href="{{SiteWorflowLink}}" 
               target="_blank">(Show all)</a></h3>
<table style="margin:10px" 
border="1" class="table table-bordered">
<tr>   
    <td style="color:black;font-weight:bold;padding:5px">WF Id</td>
    <td style="color:black;font-weight:bold;padding:5px">WF Name</td>
    <td style="color:black;font-weight:bold;padding:5px">Status Code</td>
    <td style="color:black;font-weight:bold;padding:5px">Status</td>
    <td style="color:black;font-weight:bold;padding:5px">Last Run</td>
    <td style="color:black;font-weight:bold;padding:5px">Workflow Instance Link</td>
    <td style="color:black;font-weight:bold">ErrorDetails</td>
      <tr ng-repeat="x in SiteWorkflowInstances | orderBy:'-LastRun' | 
          filter:{ StatusName:SearchStatus || undefined}">    
     <td>{{ x.WFId}}</td> 
     <td>{{ x.WFName}}</td>     
     
    <td>{{x.StatusCode}}</td>   
    <td><span ng-show="x.StatusName=='Suspended'" 
    style="color:red">{{x.StatusName}}</span>
        <span ng-show="x.StatusName!='Suspended'">
        {{x.StatusName}}</span></td>
    <td>{{x.LastRun | date :  "dd-MMM-y h:mm a" }}</td>
    <td><a href="{{x.WorkflowInstanceLink}}" 
    target="_blank">{{x.InstanceID}}</a></td>
    <td>{{x.ErrorDetails}}</td>
  </tr>
</table>
<h3 style="color:blue">Workflow Inventory</h3>
<table style="margin:10px" border="1" class="table table-bordered">
<tr>
    <td style="color:black;font-weight:bold;padding:5px">WF Name</td>
    <td style="color:black;font-weight:bold;padding:5px">WF Desc</td>
    <td style="color:black;font-weight:bold;padding:5px">WF ID</td>
    <td style="color:black;font-weight:bold;padding:5px">Published</td>
    <td style="color:black;font-weight:bold;padding:5px">WF Status</td>  
    <td style="color:black;font-weight:bold;padding:5px">RestrictToType</td>        
    <td style="color:black;font-weight:bold;padding:5px">ModifiedBy</td>
    <td style="color:black;font-weight:bold;padding:5px">RestrictToScope</td>
    <td style="color:black;font-weight:bold;padding:5px">ListName</td>
    <td style="color:black;font-weight:bold;padding:5px">AppAuthor</td>
    <td style="color:black;font-weight:bold;padding:5px">SubId</td>
</tr>
  <tr ng-repeat="x in WFInstances | 
  orderBy:['-RestrictToType','-WorkflowEntitlementType']">
    <td>{{ x.WFName}}</td>
    <td>{{ x.WFDesc}}</td>
    <td>{{x.WFId}}</td>
    <td>{{x.Published}}</td>  
    <td>{{x.WorkflowEntitlementType}}</td>   
    <td>{{x.RestrictToType}}</td>  
    
    <td>{{x.ModifiedBy}}</td>   
    <td>{{x.RestrictToScope}}</td>  
    <td>{{x.ListName}}</td>  
    <td>{{x.AppAuthor}}</td> 
    <td>{{x.SubId}}</td>      
  </tr>
</table>

</div>
</body>
</html>

JavaScript - You can put the below JavaScript in the same page or add a separate js file and provide reference in your HTML.

JavaScript
<script>
var app = angular.module("WFApp", []); 
app.controller("myCtrl", function($scope,$timeout) {
    $scope.WFInstances= [];
    $scope.WFWithListNames= [];
     $scope.ProgressDiv=true;
     $scope.QueryPastDays= GetQueryStringParams('QueryPastDays');
     if(!$scope.QueryPastDays)
     {
         $scope.QueryPastDays=30;
     }
// Query string to display results based on days, 
// by default it shows last 30 days, can be filtered after passing the query string
     $scope.SiteWorflowLink = _spPageContextInfo.webAbsoluteUrl + 
     "/_layouts/15/workflow.aspx?ShowAll=1";
     //alert($scope.QueryPastDays);
     // $timeout( function(){
         var WorkflowStatus =
               {
                   0 : "NotStarted",
                   1 : "Started",
                   2 : "Suspended",
                   3 : "Canceling",
                   4 : "Canceled",
                   5 : "Terminated",
                   6 : "Completed",
                   7 : "NotSpecified",
                   8 : "Invalid"
               };
    var context = new SP.ClientContext.get_current();
    //var context = SP.ClientContext.get_current();    
    var web = context.get_web();
    var sMgr =new SP.WorkflowServices.WorkflowServicesManager(context, web);
    var wDefs = sMgr.getWorkflowDeploymentService().enumerateDefinitions();
    //context.load(wDefs);    
    context.load(wDefs,'Include(DisplayName, Description,
                        Id,Published,AssociationUrl,Properties)');
    context.executeQueryAsync(
        function(){
        var WFInstances=[];
        var SiteWFInstances=[];
            var e = wDefs.getEnumerator();
            while (e.moveNext()) {
                var def = e.get_current();
                //alert(def.get_displayName() + " id: " + def.get_id());
                var data={};
                data.WFName=def.get_displayName();
                data.WFDesc=def.get_description()
                data.WFId=def.get_id();
                data.Published=def.get_published()
                data.URL=def.get_associationUrl();
                data.ModifiedBy=def.get_properties().vti_modifiedby.split
                                                     ('i:0#.f|membership|')[1];
                data.AppAuthor=def.get_properties().AppAuthor;
                data.SubId=def.get_properties().SubscriptionId;
                data.RestrictToScope=def.get_properties().RestrictToScope;
                data.RestrictToType=def.get_properties().RestrictToType;
                if(def.get_properties()['NWConfig.WorkflowEntitlementType']){
                data.WorkflowEntitlementType=def.get_properties()
                                             ['NWConfig.WorkflowEntitlementType'];
                }
                else {
                data.WorkflowEntitlementType="N/A";
                }
                if(data.RestrictToScope){
                     data.ListName= GetListName(data.RestrictToScope);
                 }
                 else {
                     data.ListName= "N/A";
                 }
                 if(data.WFName=="TestSiteWF" || data.WFName=="MovementRequestWF"){
                     //debugger;
                 }
                 if(data.RestrictToType){
                        WFInstances.push(data);
                        if(data.RestrictToType=="Site"){
                        SiteWFInstances.push(data);
                        }                        
                   }
            }
            $scope.WFInstances=WFInstances;
            
            //Get SIte WF Instances
            $scope.GetSiteWFInstances(SiteWFInstances)
            var AllItems=[];    
             $.each(WFInstances, function(index, dataRec) {    
             
              if(dataRec.ListName && dataRec.ListName!="N/A")
             {
                 var filter = "Modified ge '" + new moment().subtract
                 ($scope.QueryPastDays,'days').format('YYYY-MM-DD') + "T00:00:00.000Z'";
                 var GetListData= GetListItemByList
                                  (dataRec.ListName,"ID,Modified,Title", filter);

                 //var GetListData= GetListItemByList(dataRec.ListName, "ID");
                 if(GetListData.length>0)
                 {
                         var WFWithListNames=[];
                         $.each(GetListData, function(indexitem, item) {
                          var workflowServicesManager = 
                          SP.WorkflowServices.WorkflowServicesManager.newObject(context, web);
                          var workflowSubscriptionService = 
                          workflowServicesManager.getWorkflowSubscriptionService();
                          var workflowInstanceService = 
                          workflowServicesManager.getWorkflowInstanceService();
                          // find the subscription ID manually on your SharePoint site.
                          // List Setting->Workflow Setting,
                          // you can find the subscription ID by the URL of the 
                          // workflow name on this page.
                          //var subscriptionInstance = 
                          //workflowSubscriptionService.getSubscription
                          //("834ec338-ce39-49b0-a64a-ba8054422de2");
                          //context.load(subscriptionInstance);
                          var wfInstances = 
                          workflowInstanceService.enumerateInstancesForListItem
                          (dataRec.RestrictToScope, item.ID);
                          //context.load(wfInstances);
                          context.load(wfInstances,'Include
                          (Status, Id,FaultInfo,WorkflowSubscriptionId)');
                          window.count = 0;      
                          context.executeQueryAsync(
                            function(){       
                                var instancesEnum = wfInstances.getEnumerator();
                                 while (instancesEnum.moveNext()) {
                                     if(instancesEnum.get_current().
                                        get_workflowSubscriptionId()==dataRec.SubId)
                                     {
                                        var WFStatusCode = instancesEnum.get_current().
                                                           get_status();
                                        var ErrorDetails=instancesEnum.get_current().
                                                         get_faultInfo();
                                        var InstanceID=instancesEnum.get_current().get_id();
                                        var InstanceURL=_spPageContextInfo.webAbsoluteUrl + 
                                        "/_layouts/15/wrkstat.aspx?List={" + 
                                        dataRec.RestrictToScope + "&WorkflowInstanceName=" + 
                                                                  InstanceID;
                                         var ItemLink=_spPageContextInfo.webAbsoluteUrl + 
                                         "/Lists/" + dataRec.ListName + 
                                                     "/DispForm.aspx?ID=" + item.ID;
                                         
                                         var IntanceSubscriptionID= 
                                                     instancesEnum.get_current().
                                                     get_workflowSubscriptionId();
                                         //window.count = workflowInstanceService.
                                         //countInstancesWithStatus(subscriptionInstance, 1);  
                                                              
                                        var WFInstanceData={};
                                         WFInstanceData.ListName=dataRec.ListName;
                                         WFInstanceData.ItemId=item.ID;
                                         WFInstanceData.Title=item.Title;
                                         WFInstanceData.Modified=item.Modified;
                                         WFInstanceData.Status=WFStatusCode;
                                        WFInstanceData.StatusName=WorkflowStatus[WFStatusCode];
                                         WFInstanceData.WFName=dataRec.WFName;
                                         WFInstanceData.WorkflowLink=
                                                   _spPageContextInfo.webAbsoluteUrl + 
                                         "/_layouts/15/workflow.aspx?List={" + 
                                         dataRec.RestrictToScope + "}&ID=" + item.ID;
                                         WFInstanceData.WorkflowInstanceLink=InstanceURL;
                                         WFInstanceData.InstanceID=InstanceID;
                                         WFInstanceData.ErrorDetails=ErrorDetails;
                                         WFInstanceData.ItemLink=ItemLink;
                                         
                                         if(WFStatusCode>1 && WFStatusCode!=6)
                                         {
                                             AllItems.push(WFInstanceData);
                                             $scope.WFWithListNames=AllItems;
                                            $scope.$apply();  
                                        }
                                    }
                                     //console.log("Status is "+oList);
                                }              
                            },
                            function(a,b){
                                alert("Error happened when trying to get 
                                       workflow subscriptions for list");
                                  console.error(message);
                            }    
                            );       
                      });
                 }
             }
             });
            
            $scope.$apply();
        },
        function(a,b){
            alert(args.get_message());
        }        
    );     
    $scope.GetSiteWFInstances = function(SiteWFInstances)
    {
        $scope.SiteWorkflowInstances=[];
         var workflowServicesManager = 
                new SP.WorkflowServices.WorkflowServicesManager(context, web);
            //var workflowDeploymentService = 
            //workflowServicesManager.getWorkflowDeploymentService();
            var workflowInstanceService = 
                    workflowServicesManager.getWorkflowInstanceService();
            var workflowInstances = workflowInstanceService.enumerateInstancesForSite();
            context.load(workflowInstances);
            context.executeQueryAsync(function (sender, args) {
                // enumerateDefinition returns ClientCollection object
                var definitionsEnum = workflowInstances.getEnumerator();
                // Going through the definitions
                while (definitionsEnum.moveNext()) {
                    var def = definitionsEnum.get_current();
                    var SiteInstance={};
                    SiteInstance.WFId= def.get_workflowSubscriptionId();
                    $.each(SiteWFInstances, function(indexitem, item) {
                    if(SiteInstance.WFId==item.SubId) {
                        SiteInstance.WFName= item.WFName;
                        }
                    });                   
                    SiteInstance.InstanceID=def.get_id();
                    SiteInstance.ErrorDetails=def.get_faultInfo();
                    //SiteInstance.WFName=def.get_id();
                    SiteInstance.LastRun=def.get_instanceCreated();
                    SiteInstance.WorkflowInstanceLink=_spPageContextInfo.webAbsoluteUrl + 
                    "/_layouts/15/wrkstat.aspx?WorkflowInstanceName=" + def.get_id();
                    
                    SiteInstance.StatusCode= def.get_status();
                    SiteInstance.StatusName= WorkflowStatus[SiteInstance.StatusCode];     
                    if(SiteInstance.StatusCode>1 && SiteInstance.StatusCode!=6)    {
                        $scope.SiteWorkflowInstances.push(SiteInstance);
                    }
                    $scope.$apply();
                    // Displaying information about this definition - DisplayName and Id
                    //+ " User Status: " + def.get_userStatus() + 
                    " Subsc: " + def.get_workflowSubscriptionId()
                    //console.log("id: " + def.get_id() + 
                    " Status: " + def.get_status() + " Stage: " + def.get_userStatus());
                }

            }, function (sender, args) {
                alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
            });
    }    
        
    $('#ProgressDiv').hide();
    $scope.ProgressDiv=false;
//}, 10000 );
});

function GetListItemByList(ListTitle, FieldTitle, filter, link) {
    //debugger;
    if (link) {
        SiteLink = link;
    } else {
        SiteLink = _spPageContextInfo.webAbsoluteUrl;
    }
    var DataList = [];    
    if (FieldTitle) {
        var URL = SiteLink + "/_vti_bin/client.svc/web/lists/getByTitle
        ('" + ListTitle + "')/Items?$top=5000&$select=" + FieldTitle;
    } else {
        var URL = SiteURL + "/_vti_bin/client.svc/web/lists/getByTitle
        ('" + ListTitle + "')/Items?$top=5000&$select= *";
    }

    if (filter) {
        URL += "&$filter=" + filter;
    }
    $.ajax({
        url: URL,
        type: "GET",
        async: false,
        headers: {
            "accept": "application/json;odata=verbose",

        },
        success: function(data) {
            DataList = data.d.results;

        },
        error: function(error) {
            alert(JSON.stringify(error));
        }
    });

    return DataList;
}

function GetListName(guid)
{
    var ListTitle="";
    $.ajax({
        url: _spPageContextInfo.webAbsoluteUrl + 
        "/_api/web/lists('" + guid + "')",
        method: "GET",
        async:false,
        headers: { "Accept": "application/json; odata=verbose" },
        success: function (data) {
            //console.log("List Title :" + data.d.Title);
            ListTitle=data.d.Title;
        },
        error: function (data) {
           // console.log(data);
        }
  });
  return ListTitle;
}
function GetQueryStringParams(sParam) {
    var sPageURL = window.location.search.substring(1);
    var sURLVariables = sPageURL.split('&');
    for (var i = 0; i < sURLVariables.length; i++) {
        var sParameterName = sURLVariables[i].split('=');
        if (sParameterName[0] == sParam) {
            return sParameterName[1];
        }
    }
}
</script>

I have attached the HTML file with HTML and JS, just update the JS references and add CEWP to display the report.

History

  • 27th January, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)