65.9K
CodeProject is changing. Read more.
Home

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

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Jan 27, 2020

CPOL
viewsIcon

7085

downloadIcon

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.

Steps

  1. Create an empty HTML page and add the below HTML.
  2. Update js references:
<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.

<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