Wednesday, February 10, 2016

Start SharePoint 2013 List Workflow with List Item Menu Custom Action and JavaScript Client Object Model

This post is for SharePoint 2013 Visual Studio Custom Workflow. It works for SharePoint 2013 farm solution, sand boxed solution and Add-in.

 After the list workflow is deployed and associated as to be started manually, the user has to go to workflow page from the list item menu first, then start the workflow. This is not a good user experience. Not mention if there are multiple workflows associated with this list.

So it will be very convenient if user can directly start workflow in list item menu and see the message showing the workflow is started or failed to run.

To do this in Visual Studio, we need to add three items: workflow, module to include JavaScript files and custom action.



The module to include JavaScript files should be like this:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Assets" Url="SiteAssets">
    <File Path="Assets\jquery-1.9.1.js" Url="jquery-1.9.1.js" ReplaceContent="TRUE" />
    <File Path="Assets\StartWorkflow.js" Url="StartWorkflow.js" ReplaceContent="TRUE" />
  </Module>
</Elements>
You can download uptodate JQuery file from https://jquery.com/.

The two JavaScript files will be added to the site's Site Assets document library.

StartWorkflow.js code:

var dlg = null;
var itemId;
function StartWorkflow()
{
      itemId = this.currentItemID;
      var scriptbase = _spPageContextInfo.webAbsoluteUrl + "/_layouts/15/";
      $.getScript(scriptbase + "SP.js", function ()
      {
           $.getScript(scriptbase + "SP.core.js", function ()
           {
                $.getScript(scriptbase + "SP.WorkflowServices.js", Start_Workflow);
           });
      });
}

 function Start_Workflow()

{
      var subscriptionId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
      showInProgressDialog();
      var ctx = SP.ClientContext.get_current();
      var wfManager = SP.WorkflowServices.WorkflowServicesManager.newObject(ctx, ctx.get_web());
      var subscription =wfManager.getWorkflowSubscriptionService().getSubscription(subscriptionId);
      ctx.load(subscription);
      ctx.executeQueryAsync( function (sender, args)
     {
          var params = new Object();
          var formData = subscription.get_propertyDefinitions()["FormData"];
          if (formData != null && formData != 'undefined' && formData != "")
          {
               var assocParams = formData.split(";#");
               for (var i = 0; i < assocParams.length; i++)
               {
                     params[assocParams[i]] = subscription.get_propertyDefinitions()[assocParams[i]];
               }
          }
       wfManager.getWorkflowInstanceService().startWorkflowOnListItem(subscription, itemId, params);
          ctx.executeQueryAsync( function (sender, args)
          {
               closeInProgressDialog();
               alert('Workflow is Started.');
           },
           function (sender, args)
           {
                 closeInProgressDialog();
                 alert('Failed to run workflow');
           } );
       },
       function (sender, args)
       {
             closeInProgressDialog();
             alert(args.get_message());
       } );
}

 function closeInProgressDialog()

{
      if (dlg != null)
      {
           dlg.close();
      }
}

 function showInProgressDialog()

{
      dlg = SP.UI.ModalDialog.showWaitScreenWithNoClose("Please wait...", "Waiting for workflow...", null, null);
}

In above code, var subscriptionId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; will be replaced with the deployed and associated workflow's subscriptionId. I will explain this later on. 

StartWorkflowAction Elements.xml:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction ScriptSrc="~site/SiteAssets/StartWorkflow.js" Location="ScriptLink" Sequence="1"> </CustomAction>
  <CustomAction Id="CustomMenuItem.StartWorkflow"
                RegistrationType="ContentType"
                RegistrationId="0x01"
                Location="EditControlBlock"
                ImageUrl="/_layouts/IMAGES/DOCLINK.GIF"
                Sequence="600"
                Title="Start Workflow"
                Description="Start or continue workflow"
                >
    <UrlAction Url="javascript:void(StartWorkflow());" />
  </CustomAction>
</Elements>
After this is deployed and the workflow is associated, there are still two steps:

1. Get the workflow subscriptionid from workflow settings page, You can click F12 on the browser and get it from HTML code, then download StartWorkflow.js file from Site Assets document library to your local folder, replace 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' with the subscriptionid you got, save and upload it back to Site Assets document library, click CTR-F5 to refresh your browser.


 2. On the list form which workflow associated with, edit list view web part, add "~site/SiteAssets/jquery-1.9.1.js" to JS Link. The JQuery file should be the actual file you downloaded and deployed. 

 After all above are done, from the list's item menu, you will see "start workflow" item, click it, you will see the In Progress Dialog and then the message showing the workflow is started or failed to run workflow.