Starting a SPD workflow that is bound to a content type

Jul 14, 2014 at 10:50 PM
I am trying to initiate a SPD workflow that has been defined at the web's content type level. The workflow is associated to the content type and I can start it from the SP UI without issue. The item I am trying to start the workflow on is a basic list item, not a document or anything.

I get an HTTP 500 error returned and I see this in ULSViewer:

Workflow Soap: StartWorkflow failed Item: [Full URL to list]/1_.000 TemplateId: [The GUID I defined] Value cannot be null. Error: Value cannot be null.

This workflow does not have any initiation parameters. It does however use InfoPath forms for approving the assigned tasks.

Here is the code I am trying to use. For the templateId I have tried the GUID that is defined in the workflow.xoml.wfconfig.xml (there is only one GUID attribute, BaseID) and I have also tried the GUID that can be found as a query string when looking at the workflow start page (called TemplateID).
        $().SPServices({
            debug: true,
            operation: "StartWorkflow",
            templateId: "My Workflow GUID",
            async: false,
            item: "http://servername.local/sites/site/web/Lists/myList/1_.000",
            workflowParameters: "<root/>",
            completefunc: function (data) {
                var stophere = "";
            }
        });
I saw someone in the comments on the StartWorkflow documentation page say that if you are using content type defined reusable workflows (I am) that you need to walk the content type but I do not quite know how to make that work with SPServices. I tried using JSOM to load the content types but context.get_contentTypes() always returns null and I wasn't sure that would work with SPServices so I didn't spend a lot of time on it.

If someone could give me a quick pointer on how to get the workflow from the list's content type I would be appreciative.
Jul 18, 2014 at 9:53 PM
Edited Jul 18, 2014 at 9:53 PM
I figured it out. The issue is the list is wiped out each time after I debugged it, even though the content type (and workflow association) is not. This causes the TemplateID to change. You have to do a little more homework in your query. I am using Angular in my code, so the $scope stuff is an artifact of that.
    $scope.startWorkflow = function (params) {
        var tempHost = parent.window.location.hostname;
        var tempFullPath = parent.window.location.pathname;
        href = parent.window.location.protocol + "//" + tempHost + _spPageContextInfo.webServerRelativeUrl + "/Lists/{your list name}"

        var itemUrl = href + "/" + $scope.itemID + "_.000";
        var workflowGUID;
        $().SPServices({
            operation: "GetTemplatesForItem",
            item: itemUrl,
            async: true,
            completefunc: function (xData, Status) {
                var xmlDoc = $.parseXML(xData.responseText);
                $xml = $(xmlDoc);
                var workflows = $xml.find("WorkflowTemplates > WorkflowTemplate");
                var currentItemURL = this.item;
                var approvalFound = false;
                workflows.each(function (i, e) {
                    var a = $(this).attr("Name");
                    if ($(this).attr("Name") == "Approval") {//This is the workflow name as show on the content type's Workflow Settings page
                        approvalFound = true;
                        var guid = $(this).find("WorkflowTemplateIdSet").attr("TemplateId");
                        if (guid != null) {
                            workflowGUID = "{" + guid + "}";
                            //in this point, we have our workflow Id and we have to call the starting method
                            var assocData = //We are using custom InfoPath forms for our workflow. See http://sympmarc.com/2013/01/21/spservices-stories-1-how-to-start-a-workflow-on-multiple-items-in-a-list/ for how to use this

                            $().SPServices({
                                debug: false,
                                operation: "StartWorkflow",
                                templateId: workflowGUID,
                                async: true,
                                item: itemUrl,
                                workflowParameters: assocData,
                                completefunc: function (data) {
                                    var stophere = "";
                                    $scope.closeForm();
                                }
                            });
                        }
                    }
                    if (!approvalFound) {
                        alert('An error has occurred starting the Request Approval workflow. ');
                        $.unblockUI();
                    }
                });
            }
        });
    };
Marked as answer by lgoolsby on 7/18/2014 at 1:53 PM
Coordinator
Jul 19, 2014 at 8:10 AM
Sorry I didn't get back to you before you figured it out, but you arrived at exactly the right place!

M.
Oct 16, 2014 at 9:37 PM
We have been having a couple issues with the above code. Namely we occasionally see that the workflow status column will say "Failed to Start." We have also seen that sometimes it takes a really long time to start the workflow (~3-5 minutes). The majority of the time the code works fine, though.

We suspect the issue is due to the workflow being fairly complex and it takes a couple minutes for SharePoint to compile the workflow the first time its called for the day. In some scenarios this takes longer that the IIS process will allow and the thread gets killed. At least that is our current theory, its not consistent.

Does anyone know of what kind of data would be expected if I was to change the last completeFunc above to have the Status property so I could interrogate the variable for success/failure and add some logic to try again? What exactly is returned in the event of an error? Is it the workflow status or something more generic? We cannot easily replicate this issue in an environment where we can attach a debugger to see what exactly is returned in hope somebody knows. Ideally if its the workflow status we would like to look for "Failed to Start" and try a couple attempts before giving up. For example:
var loop = true;
while(loop){
    $().SPServices({
        debug: false,
        operation: "StartWorkflow",
        templateId: workflowGUID,
        async: true,
        item: itemUrl,
        workflowParameters: assocData,
        completefunc: function (data, Status) {
            if(Status == "Failed to Start"){
                  continue;//Add some logic to only try X times
            }
            else if(Status != "Success"){
                  alert(Status); 
            else{
                 //Status == "Success"?
                  loop = false;
                  $scope.closeRequest();
            }
        }
    });
};
Oct 17, 2014 at 12:37 AM
I don't think you should use a while loop here. What you could do, is check the workflow status within the completefunc of the SPServices call. If it's still processing, then fire your function to continue to check the status. If the status ever errors, then refire your entire workflow call again. That seems like it would work and keep your code from firing the StartWorkflow operation erroneously.

Cheers,
Matthew