SPServices Stories #1 – How to Start a Workflow on Multiple Items in a List

This entry is part 1 of 21 in the series SPServices Stories

Introduction

Given the fact that so many people are using SPServices these days, I wanted to start posting some of the great things that people are doing with it out there.

If you have a story about how you are using SPServices that you would like to tell, ping me using the contact form. Your story doesn’t even have to include code, though people love to see examples. I’m always interested in what impact using SPServices may have had on your development philosophy, time to market with solutions, hiring practices, really anything that you feel SPServices has enabled you to do.

We can remove any identifying details if you feel that need to do so, but I’d like these stories to show off what *you* have done, so it’s great if you can take credit. I reserve the right to do a little editing for clarity, but otherwise you can write your own story. I’m also happy to help.

The first guest post is from fellow SharePoint MVP, Alberto Diaz Martin, who lives in Spain. Thanks to Alberto for kicking this series off!

[important]You can also read this post in Spanish on Alberto’s blog SharePoint 2010. Iniciar un flujo de trabajo en múltiples elementos de una lista[/important]

How to Start a Workflow on Multiple Items on a List

In SharePoint 2010 we have the option to select multiple items on a list. When you select several items, the ribbon allows you to Delete Items and Send Alerts, but where is the Workflow command?

To start a workflow on a list item, you have to go through the Start workflow page and if the workflow has an initialization form, you also have to enter the parameters. Because of this, SharePoint doesn’t allow users to start a workflow on multiple items simultaneously. But why not do so when we have a parameter-less workflow?

I think this is a missing feature on SharePoint 2010 because we can certainly do it using the SharePoint API or Web Services without any problems. What can we do to provide this capability to our users?

First, we need to create a Ribbon command using a Custom Action and in this action we will have two options to try to start the workflow. The first one uses an application page by passing the selected items as parameters and uses the server API to start the process. The second, and more flexible and elegant option is using JavaScript and the SharePoint Web Services to start each workflow per item.

SPServices Workflow Ribbon Custom Action

SharePoint Web Services are a horrible way to talk with SharePoint [ed: I disagree, but everyone is entitled to their opinion!] because the Web Services use XML to get and put parameters and options, and it’s not easy working with XML in JavaScript.

SPServices to the Rescue!!

As you know, SPServices is a jQuery library which encapsulates SharePoint Web Services with jQuery to make it easy to call them. SPServices has a Workflow namespace with some powerful operations and we can use StartWorkflow to start an item workflow, even if it has parameters.

It is so easy to use, you only need the Item URL, the workflow template Id and, if required, the workflow parameters.

$().SPServices({
  operation: "StartWorkflow",
  item: currentItemURL,
  templateId: workflowGUID,
  workflowParameters: workflowParams,
  async: true,
  completefunc: function () {
    SP.UI.Notify.addNotification("Workflow process started on selected item.", false);
  }
});

To get the workflow template Id, we have another function called GetTemplatesForItem that returns all the associated workflows for an item. All we have to do is get all the templates and find our workflow by name.

$().SPServices({
  operation: "GetTemplatesForItem",
  item: itemURL,
  async: true,
  completefunc: function (xData, Status) {
    var currentItemURL = this.item;
    $(xData.responseXML).find("WorkflowTemplates > WorkflowTemplate").each(function (i, e) {
      if ($(this).attr("Name") == "Invoice Approve") {
        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
        }
      }
    }
  }
})

Now, we have to traverse the selected items in the custom action method, and for each item call the SPServices StartWorkflow method. Something like this:

function StarSignWorkflow(listId) {

  RemoveAllStatus(true);
  waitDialog = SP.UI.ModalDialog.showWaitScreenWithNoClose('Starting approval workflow process on selected item','Please,wait until we finished this long operation.',76,400);

  //Get the selected items
  clientContext = new SP.ClientContext.get_current();
  var web = clientContext.get_web();
  var list = web.get_lists().getById(listId);
  var items = SP.ListOperation.Selection.getSelectedItems(ctx);
  totaSelItems = items.length;

  //Because only have items Id,we need to use Client Object Model to get EncodeAbsUrl.
  var query = new SP.CamlQuery();
  var queryString = '<View><Query><Where><In><FieldRef Name="ID"/><Values>';
  for (index in items) {
    var valueString = '<Value Type="Integer">' + items[index].id + '</Value>';
    queryString = queryString + valueString;
  }

  query.set_viewXml(queryString + '</Values></In></Where></Query></View>');
  this.collListItems = list.getItems(query);
  clientContext.load(collListItems,'Include(EncodedAbsUrl)');

  //In the success callback,we’ll have all the selected items with the absolute url.
  clientContext.executeQueryAsync(Function.createDelegate(this,this.onInitProcessSuccess),Function.createDelegate(this,this.onInitProcessFail));
}

function onInitProcessSuccess() {

  var listItemEnumerator = this.collListItems.getEnumerator();

  //If our workflow has default init param,we can provide it in this way to run workflow with default values.
  var workflowParams = "<Data><Approvers></Approvers><NotificationMessage></NotificationMessage>" +
      "<DurationforSerialTasks></DurationforSerialTasks><DurationUnits></DurationUnits>" +
      "<CC></CC><CancelonRejection></CancelonRejection><CancelonChange></CancelonChange>" +
   "<EnableContentApproval></EnableContentApproval></Data>";

  try {
    var counter = 1;
    var total = totaSelItems;

    //Traverse all the selected items
    while (listItemEnumerator.moveNext()) {
      var oListItem = listItemEnumerator.get_current();
      var itemURL = oListItem.get_item('EncodedAbsUrl');
      var workflowGUID = null;

      //Before start the workflow,we used GetTemplatesForItem to get Workflow Template Id.
      $().SPServices({
        operation: "GetTemplatesForItem",
        item: itemURL,
        async: true,
        completefunc: function (xData,Status) {
          var currentItemURL = this.item;
          $(xData.responseXML).find("WorkflowTemplates > WorkflowTemplate").each(function (i,e) {
            if ($(this).attr("Name") == "Invoice Approve") {
              var guid = $(this).find("WorkflowTemplateIdSet").attr("TemplateId");
              if (guid != null) {
                workflowGUID = "{" + guid + "}";
                $().SPServices({
                  operation: "StartWorkflow",
                  item: currentItemURL,
                  templateId: workflowGUID,
                  workflowParameters: workflowParams,
                  async: true,
                  completefunc: function () {
                    if (total == counter) {
                      if (waitDialog != null) {
                        waitDialog.close();
                      }
                      SP.UI.Notify.addNotification("Started workflow approved process for selected invoice.",false);
                      window.location.reload();
                    }
                    counter++;
                  }
                });
              }
            }
          });
        }
      });
    }
  }catch (e) {
    if (waitDialog != null) {
      waitDialog.close();
    }
    AddStatus("There is an exception. Error: " + e.message,"red");
  }
}

As you can see, you have an easy way to provide an easy way to start a process on multiple items at the same time. Thanks to SPServices, working with SharePoint client side is more flexible and easy.

AlbertoDiazMartinAlberto Diaz Martin
MVP SharePoint
[email protected]
@adiazcan
http://geeks.ms/blogs/adiazmartin

Series NavigationSPServices Stories #2 – Charting List Data with HighCharts >>

Similar Posts

56 Comments

    1. Toby:

      You should be able to scroll left/right at the bottom of each code window. Also, if you hover over the code window, you’ll see a little toolbar in the upper right where you can copy the code as text.

      M.

  1. Hi thanks for this nice article,can you please provide the steps you have worked …it will be easy for us to learn

  2. Great article, but unable to get this to work on a document library. As Marc suggested, I did my homework, lots of it, but still no go. The basics are missing from the article, which for an expert like Marc may be okay, but for us mere mortals, some basic information would be helpful. For example, how to trigger this think. How to configure the custom action button would be helpful. What values needs to be added to the script so that it works in different environments. It would be appreciated. Thanks

  3. Hi Marc, I am getting this error :jquery-1.7.2.min.js:4 POST http://vswm:5805/sites/ShoppingCart/_vti_bin/Workflow.asmx 500 (Internal Server Error) on checking the ULS logs I get this error:Workflow Soap: StartWorkflow failed Item: http://vswm:5805/sites/ShoppingCart/RecordsLibrary/Web%20Developer%20Tools%20EULA.rtf TemplateId: 6adfe111-f615-434d-9c7c-4aa653b0ee08 Data at the root level is invalid. Line 1, position 1. Error: Data at the root level is invalid. Line 1, position 1. Please help me..

  4. Marc: I am sorry I didn’t get you… I just implemented the code you have posted in both SP2010 and SP2013 … I get this error on both the environments.. Is the SOAP XML that you are talking about? I am new to SOAP and WebService so don’t have much idea about it.. Could you please help me out?

    1. Hi Marc, I was able to solve this issue. My workflow did not have parameters so I was passing null value. Instead of that I added as the parameter and it worked. Thanks a lot for such a helpful post.

  5. Very frustrating:
    I usually work with SPServices and they are easy to implement in content editor. I tried many different approaches to get this working. Just beats me.
    – I tried adding an html button on a content editor and call the Javascript function sitting within the head tag of the script on onclick event. I keep getting XSS errors
    – So i created 2 different content editors separating html and script. Still doesn’t work.
    – I finally added the link to the script in the web part properties and it gives the same XSS error
    – I tried adding the custom view ribbon button, but i have never done a javascript run through it. In one of the question above, one of the person mentioned that they used javascript:openChildWindow(‘’) method. I added this pointing to the JScript in my Site Assets page. But nothing happens when i click the ribbon button.
    Any help would be appreciated

  6. Hello,
    I tried following what you did by calling the javascript:openChildWindow/OpenChildWindow() and I get an error saying its not defined. How do you call the function by pointing to an html page?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.