In list workflow starting button

I am working on a project at work that required a button that the user would click to kick off a process based on a field they filled in.  Normally, a WorkFlow could be assigned to a list on creation or update of an item, but in this case, I need to have the ability to kick off different WorkFlows based on the current stage the item is in and, as I mentioned, the fields filled in by the user.  The first step is to get the button in the list.

Since I am using a very restricted version of SharePoint and due to that or using a Document Library for the list, I have to use jQuery to add the button.  After some trial an error, I determined that I would use a Single Line text field to hold the value I needed to update. When the value is “Ready“, I don’t need to add the button, when it is “In Draft” I need to add the button. This gives me a text area to look for, in order to turn it into the button. In jQuery, you can use the filter function to cycle through a bunch of tags to find the one you want and then do an action to that particular item.

$("td").filter(
	function(){
		// my to do action
		}
	}
);

Once I found the correct <td>, I need to find the <td> in the table row that contains the item ID for the record that will be modified. This is done by finding the parent node and then the table row and then the particluar cell in that row. The cell is actually the 2nd one, so I set the eq value to 1, and then grab the text.

$(this).parents('tr:first').find('td').eq(1).text();

After I have the ID, I can build the button code and replace the HTML for the current <td> using the jQuery html function.

The complete code looks like this.

$("td").filter(
	function(){
		if($.text([this]) == 'In Draft') {
			var rowID = $(this).parents('tr:first').find('td').eq(1).text();
			console.info("id : " + $(this).parents('tr:first').find('td').eq(1).text());
			$(this).html('<button onclick="setReadyForReview( ' + rowID + ')" >Ready for Review</button>')
			return true;
		} 

	}
);

And the output looks something like this.Memo ListThe next step is to add functionality to the button function. But, before I can do that, I needed some information for the WorkFlow to get started.  One bit of information is the ID of the WorkFlow. In old SharePoint, apparently you could click on the WorkFlow and get the ID out of the URL. I don’t have it that simple. I had to find some code that would get the WorkFlow ID by using the WorkFlow name.  I found it with the WorkFlowAssociations property in SharePoint services. This property belongs to the SharePoint list, which the examples I found didn’t point out, so it took a little fiddling to figure out the correct syntax before the executeQueryAsync happened.  After the query comes back, then it is just a matter of looping through the WorkFlow list to find the one with exactly the correct name.  If you modify a WorkFlow, the previous versions are renamed, not overwritten. The list will bring back all of those versions.  I don’t have an issue with this because I will need all WorkFlows later in order to run more than one of them on the button click.

workflow: Set Ready for Review (Previous Version:7/9/2015 2:41:22 PM) id:061d2def-cf74-4ae0-a928-529e0ce7142e
workflow: Set Ready for Review id:95e3b55d-b22d-41d5-af50-e5234639fee4

Once I have the correct GUID (that long alphanumeric value with dashes) for the correct WorkFlow, I need to get the URL for the item I want to run the WorkFlow on.  For regular list, the URL has the item ID in it and looks something like this.

"https://server/site/Lists/" + ItemID + "_.000",

For Document Library lists, the end is the document file name. But, you need to make sure you have the correct URL, so you can get that by using SPServices code. The call for the GetListItems uses CAML to narrow down the results.  Since I already know the ID for the item, I just set the CAML query to look for the matching item ID.

var camlQuery = "<Query><Where><Eq><FieldRef Name='ID' /><Value Type='Text'>" + itemID + "</Value></Eq></Where></Query>"

The shown fields are more than I need, but maybe later I will need them, so I left them as an example of what else can be retrieved for the item. Here is the code for calling GetListItems operation.

var camlViewfields = "<ViewFields>"+
	'<FieldRef Name="ID"/>'+
	'<FieldRef Name="Title"/>'+
	'<FieldRef Name="LinkFilenameNoMenu"/>'+
	'<FieldRef Name="Created"/>'+
	'<FieldRef Name="EncodedAbsUrl"/>'+
"</ViewFields>";
var camlQuery = "<Query><Where><Eq><FieldRef Name='ID' /><Value Type='Text'>" + itemID + "</Value></Eq></Where></Query>"
$().SPServices({
	operation: "GetListItems",
	async: false,
	listName: "Memo Requests",
	CAMLViewFields: camlViewfields,
	CAMLLimit: "l",
	CAMLQuery: camlQuery,
	completefunc: function (xData, Status) {
// something to do when complete
});

After the list comes back, then I do the loop (of 1 row) to get the EncodedAbsUrl value for the item.

$(xData.responseXML).SPFilterNode("z:row").each(function() {
var currentItemURL = $(this).attr("ows_EncodedAbsUrl");
});

Now that I have the WorkFlow GUID and the item’s URL, I just need to create the string to pass in a value to the parameter “Status” that I added when I created the WorkFlow in SharePoint Designer. And do a little bit of tweaking to the WorkFlow GUID so that it is in the correct format.

var workflowGUID = "{" + $.local.SetReadyForReview + "}";
var workflowParams = "<Data><Status>Ready</Status></Data>";

Finally, I put it all together in order to kick off the workflow using the SPservices StartWorkFlow operation.

var camlViewfields = "<ViewFields>"+
		'<FieldRef Name="ID"/>'+
		'<FieldRef Name="Title"/>'+
		'<FieldRef Name="LinkFilenameNoMenu"/>'+
		'<FieldRef Name="Created"/>'+
		'<FieldRef Name="EncodedAbsUrl"/>'+
	"</ViewFields>";
	var camlQuery = "<Query><Where><Eq><FieldRef Name='ID' /><Value Type='Text'>" + itemID + "</Value></Eq></Where></Query>"
	$().SPServices({
	operation: "GetListItems",
	async: false,
	listName: "Memo Requests",
	CAMLViewFields: camlViewfields,
	CAMLLimit: "l",
	CAMLQuery: camlQuery,
	completefunc: function (xData, Status) {
		$(xData.responseXML).SPFilterNode("z:row").each(function() {
			var currentItemURL = $(this).attr("ows_EncodedAbsUrl");
			var workflowGUID = "{" + $.local.SetReadyForReview + "}";
			var workflowParams = "<Data><Status>Ready</Status></Data>";
			$().SPServices({
				operation: "StartWorkflow",
				item: currentItemURL,
				templateId: workflowGUID,
				workflowParameters: workflowParams,
				async: true,
				completefunc: function () {
					SP.UI.Notify.addNotification("Started workflow process.",false);
					window.location.reload();
				}
			});
		});
	}
});

When I click on the button in the WorkFlow kicks off for that particular item. I am hoping that it will be easy enough to kick off multiple WorkFlows in the same button function. I might have to do some timers or something, but I hope not. If so, it will help solve my Asynchronously Annoyed problem.

About DeanLogic
Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.

About DeanLogic

Dean has been playing around with programming ever since his family got an IBM PC back in the early 80's. Things have changed since BASICA and Dean has dabbled in HTML, JavaScript, Action Script, Flex, Flash, PHP, C#, C++, J2ME and SQL. On this site Dean likes to share his adventures in coding. And since programming isn't enough of a time killer, Dean has also picked up the hobby of short film creation.