Click here to Skip to main content
15,890,897 members
Articles / Productivity Apps and Services / Sharepoint / SharePoint 2013
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
27 Jan 2020CPOL 6.5K   107   1
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)


Written By
Software Developer (Senior)
United Arab Emirates United Arab Emirates
I am having around 8 years of experience in SharePoint development, mostly on Client side coding using Angularjs, JQuery, ReactJs. Experience in Nintex forms and workflows for SharePoint online (O365). Worked in different domains like Banking, Automotive, Oil & Gas etc.Now Working on SPFx in SharePoint online using React and SharePoint modern experience.

Comments and Discussions

 
QuestionHow would this work in SharePoint Online if you are using a content editor webpart Pin
Larry Pfaff23-Aug-22 7:12
Larry Pfaff23-Aug-22 7:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.