Completing a Workflow Task Using SPServices

Oct 15, 2011 at 12:31 AM

In the midst of developing a fully operable dashboard, I wanted to give users the alternative of completing tasks assigned to them through the custom UI without having to navigate to an EditForm page. I was curious to see if it is possible to complete a workflow task through the use of SPServices. Updating the standard fields (Status, % Complete, etc.) does not effectively end the wait for the task completion or change the Outcome column (which does not appear to be update-able through SPServices). I see this server-side coded in many examples as using either of the two following techniques...

  • SPWorkflowTask.AlterTask method
  • Changing the WorkflowTaskStatusComplete value

Let me know if this is something that has been considered/attempted/successfully implemented or just outside of the scope of SPServices as I look for a functional alternative. Thank you for this awesome utility, it's incredibly increased the accessibility of SharePoint and the ability to functionally use data.

Oct 18, 2011 at 2:50 PM

I believe this can be achieved through the AlterToDo method of the Workflow Web Service (added 0.6.0 - http://spservices.codeplex.com/releases/view/55660) as this calls AlterTask to complete a task. I've tried the following code without success...

     $().SPServices({
            operation: "GetToDosForItem",
            item: "http://domain/mysite/doclib/document.xml",
            async: false,
            completefunc: function (xData, Status) {
                    var updatedToDo = xData.responseXML.xml;
                    updatedToDo = updatedToDo.replace('ows_Completed="0"', 'ows_Completed="1"');
                    $().SPServices({
                            operation: "AlterToDo",
                            async: false,
                            item: "http://domain/mysite/doclib/document.xml",
                            taskData: updatedToDo,
                            completefunc: function (xData, Status) {
                                    alert(Status);
                            }
                    });
            }
    });

I can affectively pull the information and the AlterToDo completes with error. Anyone have any examples of how to properly use the AlterToDo method?

Oct 18, 2011 at 4:43 PM

Solved

Here's how I did it (respItemURL was passed in from another process)...

   $().SPServices({
    operation: "GetToDosForItem",
    item: respItemURL,
    async: false,
    completefunc: function (xData, Status) {
     var respToDoID = '';
     var respToDoListID = '';

     $(xData.responseXML).find("[nodeName='z:row']").each(function() {
      respToDoID = $(this).attr("ows_ID");
      respToDoListID = $(this).attr("ows_TaskListId");
     });

     $().SPServices({
      operation: "AlterToDo",
      async: false,
      todoId: respToDoID,
      todoListId: respToDoListID,
      item: respItemURL,
      taskData: '<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD" >' +
        '<my:Status>Completed</my:Status>' +
        '<my:PercentComplete>1.00000000000000</my:PercentComplete>' +
        '<my:WorkflowOutcome>Completed</my:WorkflowOutcome>' +
        '<my:FormData>Completed</my:FormData>' +
        '<my:Completed>1</my:Completed>' +
       '</my:myFields>',
      completefunc: function (xData, Status) {
       alert(Status);
      }
     });

    }
   });

Hope this helps someone else.

Coordinator
Oct 19, 2011 at 12:16 AM

VB:

Sorry I didn't get back to you, but thanks for posting your solution! It will make a great example for the GetToDosForItem and AlterToDo operations.

M.

Oct 31, 2011 at 4:41 PM

No worries, it's great to have you even check out the code, thanks for your time!

Feb 7, 2012 at 9:12 AM

Hi Marc,

I was surprised (and pleased!) to find a jquery function for completing a task!  Thanks to VisualBacon for posting sample code.  Unfortunately, I'm having trouble getting it to work.

I'm using jquery-1.7.1.min.js and jquery-SPServices-0.7.0.min.js on SP2010.  The .aspx below is invoked by a custom ribbon button, and its purpose is to retrieve a task list item attribute, then redirect to another page, but while I'm at it I'd like to complete the task as well.

The section which retrieves the list item attribute works fine, as does the redirect.  When I add the CompleteTask() function, I get this error:

soap:ServerException of type 'Microsoft.SharePoint.SoapServer.SoapServerException' was thrown.Specified method is not supported.
status:  500
statusText:  Internal Server ERror

Any thoughts?

Code:

<%@ Page Language="C#"  MasterPageFile="~masterurl/default.master" %>

<asp:Content runat="server"  ID="Main" ContentPlaceHolderID="PlaceHolderMain">
<script type="text/javascript" src="~/js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="~/js/jquery.SPServices-0.7.0.min.js"></script>

<script type="text/javascript">
    $(document).ready(function () {
        tasknbr = $.getUrlVar('TaskID');
        ItemUrl = $.getUrlVar('URL');
        taskaction = $.getUrlVar('OP');
        var query = "";
        step = "lookup";
        NPRID = "";
        listGUID = "{yyy}";

 // Tasknbr is Item ID, passed in by custom ribbon button on EditForm.aspx
        if (tasknbr == null) {
            //      alert("Task Number Required");
            tasknbr = "588";
        }

 // ItemUrl is passed in by custom ribbon button on EditForm.aspx (but hardcoded for testing)
        if (ItemUrl == null) {
            //      alert("Task Number Required");
            ItemUrl = "Lists/xxxxxxxxxxxxxx/587_.000";
        }

 // Taskaction is Approve/Reject, passed in by custom ribbon button on EditForm.aspx (but hardcoded for testing)
        if (taskaction == null) {
            //      alert("Task Number Required");
            taskaction = "Approve";
        }

 // Retrieve the list item
        if (tasknbr != null) {
            query = "<Query><Where><Eq><FieldRef Name='ID' /><Value Type='Counter'>" + tasknbr + "</Value></Eq></Where></Query>";
            wsURL = "http://xxxxxxxx/xxx/_vti_bin/lists.asmx";
            var soapEnv =
                "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> \
                    <soap:Body> \
                        <GetListItems xmlns='http://schemas.microsoft.com/sharepoint/soap/'> \
                        <listName>{yyy}</listName> \
                        <viewName>{zzz}</viewName> \
                        <query>" + query + "</query> \
                        <viewFields></viewFields> \
                        </GetListItems > \
                        </soap:Body> \
                        </soap:Envelope>";

            // Do the web service call asynchronously.
            $.ajax({
                url: wsURL,
                type: "POST",
                async: true,
                dataType: "xml",
                data: soapEnv,
                complete: ListComplete,
                contentType: "text/xml; charset=\"utf-8\""
            });
        }
    });

    function ListComplete(xData, Status) {
        if (xData.statusText != 'OK' && xData.statusText != 'success') {
            alert(xData.statusText);
            error = "true";
        }
        $(xData.responseXML) .find("z\\:row, row").each(function (i) {
            ProcessTask(this);
        });
    }

    function ProcessTask(obj) {
        if ($(obj).attr("ows_NPR_x0020_ID") != null) {
            NPRID = $(obj).attr("ows_NPR_x0020_ID");
            CompleteTask();
        }
    }

    // All requests finished - move to next step!
    $(document).ajaxStop(function () {
        if (step != null && step == "lookup") {
            var refstring = "";
            if (NPRID != null && NPRID > '') {
                refstring = "http://xxxxxxxxx/xxx/Lists/xxxxxxxxxxxx/Project%20Tasks.aspx?&FilterField1=NPR%5Fx0020%5FID&FilterValue1=" + NPRID;
                window.location.replace(refstring);
            }
            else {
                refstring = "http://xxxxxxxxx/xxx";
                window.location.replace(refstring);
            }
        }
    });


    function CompleteTask() {
        $().SPServices({
        operation: "AlterToDo",
        async: true,
        todoId: tasknbr,
        todoListId: listGUID,
        item: ItemUrl,
        taskData: '<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD" >' +
        '<my:Status>Completed</my:Status>' +
        '<my:PercentComplete>1.00000000000000</my:PercentComplete>' +
        '<my:WorkflowOutcome>Completed</my:WorkflowOutcome>' +
        '<my:FormData>Completed</my:FormData>' +
        '<my:Completed>1</my:Completed>' +
       '</my:myFields>',
      completefunc: ProcessComplete,
     });

    }

function ProcessComplete(xDataComplete, StatusComplete) {
       alert(StatusComplete);
      }


// Retrieve query string (from http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html)
     $.extend({
        getUrlVars: function () {
            var vars = [], hash;
            var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
            for (var i = 0; i < hashes.length; i++) {
                hash = hashes[i].split('=');
                vars.push(hash[0]);
                vars[hash[0]] = hash[1];
            }
            return vars;
        },

        getUrlVar: function (name) {
            return $.getUrlVars()[name];
        }
    });
</script>
</asp:Content>

Coordinator
Feb 7, 2012 at 4:01 PM

sjohns:

In the completefunc for the AlterToDo, add an alert like this:

      completefunc: function (xData, Status) {
        alert(xData.responseText);
      }

This will show you the response back from the Web Service and may point out what the issue is.

BTW, you might want to use SPServices for the GetListItems call as well.

M.

Feb 7, 2012 at 4:30 PM

Thanks, Marc. And yeah, I normally would have used SPServices to retrieve the list item, but am using new versions of jquery and SPServices, and didn't find out about the new way to parse the list entries until after I had rewritten it the old way, without SPServices.  Didn't change it because I'm not having trouble with the attribute I'm retrieving.  But yes, ordinarily, it's nicer to use SPServices for GetListItems!

I did take a look at the response XML, but maybe I didn't include the right details?  It didn't tell me anything that I could work through right off the bat - I'm a relative newbie so if the error message is at all confusing then it will take me awhile.  Thought it might be easier for you : )  Some highlights:

xData.responseXML.text: soap:ServerException of type 'Microsoft.SharePoint.SoadServerException' was thrown. Specified method is not supported.

xData.responseXML.status: 500

xData.responseXML.statusText: Internal Server Error

xData.responseXML.childNodes.[Raw View].[Methods].nextNode():  nextNode()

xData.responseXML.childNodes.[Raw View].[Methods].reset:  reset()

xData.responseXML.childNodes.[Raw View].[Methods].item:  Invalid number of parameters

xData.responseXML.childNodes.[Raw View].[Methods].length:  2

and the response text:

 <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="soap:ServerException" _mce_href="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Exception">http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Exception of type 'Microsoft.SharePoint.SoapServer.SoapServerException' was thrown.</faultstring><detail><errorstring xmlns="Specified" _mce_href="http://schemas.microsoft.com/sharepoint/soap/">Specified">http://schemas.microsoft.com/sharepoint/soap/">Specified method is not supported.</errorstring></detail></soap:Fault></soap:Body></soap:Envelope>

Sherri

Feb 7, 2012 at 7:50 PM

Think I found the answer, it's that even though I am using a task list, and using content type "Task', my task is actually a SharePoint ListItem.

ExtendedProperties is specifically a Workflow Task property.