How to Get Workflow Definitions Using the REST API
This post will give an example of getting all 2010 and 2013 workflow definitions, using the gd-sprest library to interact with the REST API.
Overview of Demo Environment
For this code example, I will create 8 total workflows:
- List Workflows
- 2010 Published
- 2010 Un-Published
- 2013 Published
- 2013 Un-Published
- Site Workflows
- 2010 Published
- 2010 Un-Published
- 2013 Published
- 2013 Un-Published
What the workflows do are irrelevent, since our goal is to identify them using the REST API.
List Workflows (Published)
All Workflows
Reference Library
Reference the Getting Started link for additional information about the library. We will first need to reference it, so in the console tab of the browser enter the following to load the library.
var s = document.createElement("script"); s.src = "https://unpkg.com/gd-sprest/dist/gd-sprest.min.js"; document.head.appendChild(s);
The SharePoint page will need to be classic, not modern in order for the requests to work. This is due to the _spPageContextInfo variable no longer available in modern pages.
2010 Workflow Definitions
We will first review how to get the 2010 workflows.
Workflows List
In SharePoint Designer, you are able to view the Workflows and its files through the “All Files” navigation. Notice that only the 2010 workflows are displayed, and they include the unpublished workflows. The WorkflowAssociations
property of the Web
or List
object can also be used to get the associated workflow information, but this will be for published workflows only.
1. Get the Workflows
List Items
These files can be found in the “Workflows” list. The first step will be to read the items in the workflows list.
// Get the items
$REST.List("Workflows").Items().execute(function(items) {
// Parse the items
for(var i=0; i<items.results.length; i++) {
var item = items.results[i];
// Get the associated file
item.File().execute(file => {
// Analyze the file
analyze2010File(file);
});
}
});
2. Get the Files
The 3 files that will be available are:
- .xoml
- .xoml.wfconfig.xml
- .xsn
We are interested in the .xoml.wfconfig.xml
file, which contains the workflow definition.
function analyze2010File(file) {
// See if this is the target file
if(/.xoml.wfconfig.xml$/.test(file.Name)) {
// Read the file
file.content().execute(function(content) {
// Convert the buffer to a string
var wfDefinition = (new TextDecoder("utf-8")).decode(content);
// Parse the xml
var xmlDoc = (new DOMParser()).parseFromString(wfDefinition, "text/xml");
// Analyze the workflow definition
analyze2010Workflow(xmlDoc);
});
}
}
3. Determine Status
Now that we have the definition information, we can validate that it’s published.
function analyze2010Workflow(xmlDoc) {
// Get the template
var template = xmlDoc.querySelector("Template");
if(template) {
// Read the workflow information
var wfBaseId = template.getAttribute("BaseID").replace(/^{|}$/g, '').toLowerCase();
var wfName = template.getAttribute("Name");
var wfScope = template.getAttribute("Category");
var wfPublished = template.getAttribute("Draft") == "false" ? true : false;
// Default the enabled flag, we will determine this next
var wfEnabled = false;
// See if there is an association and we are targeting a list
var association = xmlDoc.querySelector("Association");
if(association && wfScope == "List") {
// Get the associated list
$REST.Web().Lists().getById(association.getAttribute("ListID"))
// Include the workflow associations
.query({
Expand: ["WorkflowAssociations"]
}).execute(list => {
// Parse the workflows association w/ this list
for(var i=0; i<list.WorkflowAssociations.results.length; i++) {
var wf = list.WorkflowAssociations.results[i];
// See if this is the target workflow
if(wf.BaseId.toLowerCase() == wfBaseId) {
// Set the enabled flag
wfEnabled = wf.Enabled;
// Compare the name
if(wf.Name == wfName) { break; }
}
}
// Log
console.log("Workflow was found in the associated list.", wfName, wfScope, wfPublished, wfEnabled);
}
);
}
// Else, this is a site workflow
else {
// Get the workflow associations for the web
$REST.Web().WorkflowAssociations().execute(function(workflows) {
// Parse the workflows association w/ this web
for(var i=0; i<workflows.results.length; i++) {
var wf = workflows.results[i];
// See if this is the target workflow
if(wf.BaseId.toLowerCase() == wfBaseId) {
// Set the enabled flag
wfEnabled = wf.Enabled;
// Compare the name
if(wf.Name == wfName) { break; }
}
}
// Log
console.log("Workflow was found in the associated site.", wfName, wfScope, wfPublished, wfEnabled);
});
}
}
}
4. Run Code
Now that we have the code completed, we will run it in the console browser. Paste the above functions first, prior to running the main function in step 1.
2013 Workflow Definitions
In a similar method, we will now get the 2013 workflows.
wfSvc List
There is a hidden wfSvc
list that will contain the files shown above from SharePoint Designer.
1. Get the Active Workflows
The first step is to get the active workflows from the REST endpoint.
$REST.WorkflowSubscriptionService().enumerateSubscriptions().execute(function(workflows) {
var activeWorkflows = {};
// Parse the active workflows
for(var i=0; i<workflows.results.length; i++) {
var workflow = workflows.results[i];
// Add the workflow
activeWorkflows[workflow.Name] = workflow;
}
// Read the wfsvc list
readWFSvcList(activeWorkflows);
});
2. Get the wfSvc
List Items
We will add the logic for getting the workflow items from the hidden list.
function readWFSvcList(activeWorkflows) {
// Get the items
$REST.List("wfSvc").Items().execute(function(items) {
// Parse the items
for(var i=0; i<items.results.length; i++) {
var item = items.results[i];
// Analyze the item
analyze2013Item(activeWorkflows, item);
}
});
}
3. Get the Files
We are interested in the .xaml
file, which contains the workflow definition.
function analyze2013Item(activeWorkflows, wfInfo) {
// Get the associated file
wfInfo.File().execute(file => {
// See if this is the target file
if(/.xaml$/.test(file.Name)) {
// Read the file
file.content().execute(function(content) {
// Convert the buffer to a string
var wfDefinition = (new TextDecoder("utf-8")).decode(content);
// Parse the xml
var xmlDoc = (new DOMParser()).parseFromString(wfDefinition, "text/xml");
// Analyze the workflow definition
analyze2013Workflow(activeWorkflows, xmlDoc, wfInfo);
});
}
});
}
4. Determine Status
The REST API has an endpoint for getting the 2013 Workflow for a list or web. We can use this to help determine the workflow state.
function analyze2013Workflow(activeWorkflows, xmlDoc, wfInfo) {
// Get the activity
var activity = xmlDoc.querySelector("Activity");
if(activity) {
// Get the workflow information
var wfName = wfInfo["WSDisplayName"];
var wfEnabled = wfInfo["WSEnabled"] ? true : false;
var wfPublished = wfInfo["WSPublishState"] == 3;
var wfScope = "";
// Get the active workflow
var workflow = activeWorkflows[wfName];
if(workflow) {
var wfListId = null;
var wfListName = null;
var wfWebUri = null;
// Parse the properties
for(var i=0; i<workflow.PropertyDefinitions.results.length; i++) {
var prop = workflow.PropertyDefinitions.results[i];
switch(prop.Key) {
case "WSEnabled":
wfEnabled = prop.Value.toLowerCase() == "true" ? true : false;
break;
case "Microsoft.SharePoint.ActivationProperties.ListId":
wfListId = prop.Value;
break;
case "Microsoft.SharePoint.ActivationProperties.ListName":
wfListName = prop.Value;
break;
case "CurrentWebUri":
wfWebUri = prop.Value;
break;
}
}
// Set the scope
wfScope = wfListId ? "List" : "Site";
// See if this is associated w/ a list
if(wfListId) {
// Log
console.log("Workflow was found associated to a list.", wfName, wfScope, wfPublished, wfEnabled);
} else {
// Log
console.log("Workflow was found associated to a site.", wfName, wfScope, wfPublished, wfEnabled);
}
} else {
// Log
console.log("Workflow was found.", wfName, wfScope, wfPublished, wfEnabled);
}
}
}
5. Run Code
Now that we have the code completed, we will run it in the console browser. Paste the above functions first, prior to running the main function in step 1.