This article first appeared as an email response to one of our clients. He described a very common use case where you have two libraries of documents, the first library is where the users work with draft documents and the other library stores the final versions of documents. In this case, users want to be able to select finished documents and move it in one click. The obvious solution for this is to use a workflow that will move documents to the final Document Library. But there is one question, how to start the workflow on the selected list items? When I was thinking about the answer to that question, I realized that it is not as trivial as it seems, because a list level workflow can be begun only on a single document. How to start multiple workflows on selected documents? In the article, I will describe how to implement such a solution.
I divided this article into the following three main parts:
- Get selected items via JavaScript
- Start SharePoint 2013 workflow from JavaScript
- Add a custom button to the list ribbon
Before we start, I want to show what the result will be. In the figure below you can see the custom button on the ribbon that will launch a workflows on selected documents.
Figure 1: Ribbon New Button
How to get selected elements via JavaScript
First I will describe how to get selected list items from a SharePoint list. To do this, you can use the special JavaScript function getSelectedItems. Please see the example below:
- var ctx = SP.ClientContext.get_current();var selectedItems = SP.ListOperation.Selection.getSelectedItems(ctx);
- for (item in selectedItems)
- {
- var itemId = selectedItems[item].id;
- console.log(itemId);
- }
As a result of execution, this snippet of code shows the IDs of selected list items in the console log.
How to start workflow
Since SharePoint 2013, Microsoft has added a new workflow engine. The new JavaScript API for manipulation of workflows also became available. In this article, I will not describe it, but if you are interested, you can find more information from the Andrei Markeev's article. I will show you a ready-to-use JavaScript snippet that can run a workflow on a single list item.
- function startWorkflow(itemID, subID)
{ var context = SP.ClientContext.get_current(); var web = context.get_web();
- var wfServiceManager = SP.WorkflowServices.WorkflowServicesManager.newObject(context, web);
- var subscription = wfServiceManager.getWorkflowSubscriptionService().getSubscription(subID);
-
- context.load(subscription);
- context.executeQueryAsync(
- function(sender, args){
- console.log("Subscription load success. Attempting to start workflow.");
- var inputParameters = {};
- wfServiceManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemID, inputParameters);
-
- context.executeQueryAsync(
- function(sender, args){ console.log("Successfully starting workflow."); },
- function(sender, args){
- console.log("Failed to start workflow.");
- console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
- }
- );
- },
- function(sender,args){
- console.log("Failed to load subscription.");
- console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
- }
- );
- }
This simple JavaScript function launches a workflow on a list item. The workflow subscription is specified by SubID argument. To identify the subscription ID of your workflow you can navigate to the workflows start page and see the URL in your browser, it should look like this (the bold GUID is the workflow subscription ID): “javascript:StartWorkflow4('6eb43e78-6e6c-486a-9147-3e3870f3a44e', '19', '{FA41C64B-42CD-4A3F-A1AF-CF674AB35C57}')”.
Figure 2: Log Workflow Link
How to Add a Button to the SharePoint Ribbon
A very important part is how a user will interact with our system. I believe that the user experience is very important for any system. In our case our plan is for the user to select the documents and click on the ribbon button that will move them to another Document Library.
There are various ways to add a button on the ribbon, but in this article, I want to describe the simplest. We will use SharePoint Designer to do this. When we use SharePoint Designer we have some limitations, for example we cannot add a new ribbon tab or hide an existing button, but from another point of view if we need to just add a button to the ribbon then it will take five minutes of our time and it doesn't require programming skills.
Please open the SharePoint Designer, navigate to “List and Libraries” and choose your Document Library.
Figure 3: SharePoint Designer Open Document Libraries
To add a ribbon button please click inside the “Custom Actions” area and choose in the ribbon “Custom Actions” - “View Ribbon”.
Figure 4: SharePoint Designer Open Document Libraries Custom Actions
When you fill in all the fields, you should see something like on the figure below in your list.
Figure 5: Ribbon CustomAction Filled
Please pay attention to the property “Navigate URL”, I filled it with the following text:
- javascript:PlumsailDemo.WFPack.API.StartListWorkflowOnSelectedItems("{6eb43e78-6e6c-486a-9147-3e3870f3a44e}");
This is a call of our function with the argument “Subscription ID” of our workflow.
Join all of it together
OK, we almost did it. All we need to do is to combine and place the JavaScript in SharePoint. To add our JavaScript on the page we will use the ScriptEditor Web part. To do this you need to enter into edit page mode. Click on “Add a Web Part” and select “Script Editor” in the “Media and Content” group.
Figure 6: Script Editor WebPart
I changed the JavaScript file a little bit to simplify using:
- < script type = "text/javascript" >
-
- PlumsailDemo.WFPack.API = (function() {
- var self = this;
- self.Context = null,
- self.WFManager = null;
-
- SP.SOD.executeFunc("sp.js", "SP.ClientContext", function() {
- SP.SOD.registerSod('sp.workflowservices.js', SP.Utilities.Utility.getLayoutsPageUrl('sp.workflowservices.js'));
- SP.SOD.executeFunc('sp.workflowservices.js', "SP.WorkflowServices.WorkflowServicesManager",
-
- function() {
- self.Context = SP.ClientContext.get_current();
- var web = self.Context.get_web();
- self.WFManager = SP.WorkflowServices.WorkflowServicesManager.newObject(self.Context, web);
- });
- });
-
- StartListWorkflowOnSelectedItems = function(subID) {
- var selectedItems = SP.ListOperation.Selection.getSelectedItems(self.Context);
-
- for (item in selectedItems) {
- var itemId = selectedItems[item].id;
- self.StartListWorkflow(itemId, subID);
- }
- };
-
- StartListWorkflow = function(itemID, subID) {
- var subscription = self.WFManager.getWorkflowSubscriptionService().getSubscription(subID);
-
- self.Context.load(subscription);
- self.Context.executeQueryAsync(
-
- function(sender, args) {
- var inputParameters = {};
-
- self.WFManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemID, inputParameters);
-
- self.Context.executeQueryAsync(
-
- function(sender, args) {
- var message = "The workflow " + subscription.get_name() + " was started on item with ID " + itemID;
- SP.UI.Notify.addNotification(message, false);
- },
-
- function(sender, args) {
- var message = "Failed to start workflow " + subscription.get_name() + " on item with ID " + itemID;
- SP.UI.Notify.addNotification(message, false);
- console.log("Failed to start workflow.");
- console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
- });
- },
-
- function(sender, args) {
- var message = "Failed to load subscription " + subID;
- SP.UI.Notify.addNotification(message, false);
- console.log("Failed to load subscription.");
- console.log("Error: " + args.get_message() + "\n" + args.get_stackTrace());
- });
-
- };
-
- return {
- StartListWorkflow: StartListWorkflow,
- StartListWorkflowOnSelectedItems: StartListWorkflowOnSelectedItems
- };
- })();
-
Conclusion
In this article, we reviewed how to start multiple workflows on the selected list items. In conclusion, I want to mention about one little detail, in such approach it is very difficult to monitor the status and errors of the workflows because it works on multiple list items. But you can extend my simple JavaScript with some workflow tracking logic if you have enough JavaScript/SharePoint skills.
You need to understand what will be the load for the system. For small systems this is an acceptable approach, but for highly loaded systems this is not so good. You can consider other approaches for moving documents, like a single-site level workflow or a custom coded solution.
As an alternative to this approach, you can start a site-level workflow and pass to it the selected IDs, but this is a theme for another article.
Please feel free to comment, I will be happy to answer to your questions.