How to launch a workflow on more than one list item simultaneously


A very common question that pops up during my training sessions is how does one automatically launch a workflow instance on a list item.

To do this, first and foremost create a Custom Action and display it on the Ribbon. The following xml added to an Empty Element in a SharePoint Project will give us the Custom Action.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <CustomAction Id="ScriptSource1"
                  ScriptSrc="Workflows/RibbonActionCommands.js"
                  Location="ScriptLink"
                  Sequence="100">
    </CustomAction>

    <CustomAction Id="StartWorkflowAction"
                  Location="CommandUI.Ribbon"
                  RegistrationType="List"                  
                  RegistrationId="101">
        <CommandUIExtension>
            <CommandUIDefinitions>
                <CommandUIDefinition Location="Ribbon.Documents.Workflow.Controls._children">
                    <Button Id="StartWorkflowButton1"
                            Image16by16="/_layouts/Images/Workflows/Check_16x16.png"
                            Image32by32="/_layouts/Images/Workflows/Check_32x32.png"
                            LabelText="Start Approval Workflow"
                            Command="StartWorkflowCommand"
                            Sequence="10000"
                            TemplateAlias="o1"/>
                </CommandUIDefinition>
            </CommandUIDefinitions>
            <CommandUIHandlers>
                <CommandUIHandler Command="StartWorkflowCommand"
                                  CommandAction="javascript:RedirectToWorkflowLaunchPage('{SiteUrl}', '{ListId}')"
                                  EnabledScript="javascript:CheckSelectedItems()">                    
                </CommandUIHandler>
            </CommandUIHandlers>
        </CommandUIExtension>
    </CustomAction>                  
</Elements>

Add the Layouts mapped folder to the solution and in it, add a JS file (named RibbonActionCommands.js in this example) which will hold the JavaScript code for the Ribbon Button. Add another application page called “WorkflowLauncher.aspx” which will contain the C# code to programmatically launch a workflow on the selected list items.

image

In the RibbonActionCommands.js file, add the following JavaScript code –

function RedirectToWorkflowLaunchPage(siteurl, listid) {
    //Some feedback to the user, while we re-direct to the application page.
    SP.UI.Notify.addNotification('Redirecting...');

    //Get a list of all the selected items (the items on which the workflow is supposed to run)
    var items = SP.ListOperation.Selection.getSelectedItems();

    //Iterate thru the array of selected items and create a comma seperated string consisting of all the selected item ids.
    var selecteditems = "";
    for (i in items) {        
        selecteditems += items[i].id + ",";
    }

    //strip out the last ','
    selecteditems = selecteditems.substr(0,selecteditems.length-1);

    //redirect the user to a dummy aspx page.
    //We pass the id of the list and the selected items in the querystring to the page
    window.location = siteurl + "/_layouts/Workflows/workflowlauncher.aspx?listid=" + listid + "&itemids=" + selecteditems;
}

function CheckSelectedItems() {
    //This function checks if more than one list item has been selected; only then does it return true. 
    //As long as this function returns false, the Ribbon Button will remain disabled.
    var items = SP.ListOperation.Selection.getSelectedItems();
    return items.length >= 1;
}
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <asp:Label runat="server" ID="lblStatus"></asp:Label>
</asp:Content>

In the code behind file, add the following code in the Page Load event handler

        protected void Page_Load(object sender, EventArgs e)
        {
            string listid = Request.QueryString["listid"].ToString();
            string itemids = Request.QueryString["itemids"].ToString();
            string[] ids = itemids.Split(',');

            //starting a workflow on list items will involve updating some infomation in the Content DBs.
            //Since these updates will be happening in response to an HTTP GET request, they will be disallowed by default
            SPContext.Current.Web.AllowUnsafeUpdates = true;

            //get a reference to the list            
            SPList list = SPContext.Current.Web.Lists[new Guid(listid)];

            //get a reference to the workflow we want to start. 
            //In this example, we assume that the workflow we need to start is named 'Approval'
            SPWorkflowAssociationCollection workflows = list.WorkflowAssociations;
            SPWorkflowAssociation association = null;
            foreach (SPWorkflowAssociation item in workflows)
            {
                if (item.Name == "Approval")
                    association = item;                
            }

            //We couldn't find a workflow named 'Approval' associated with the list/library
            if (association == null)
                lblStatus.Text = "<h3>There is no workflow named 'Approval' associated with this Library</h3>";
            else
            {
                //Start long operation
                SPLongOperation op = new SPLongOperation(this.Page);
                op.LeadingHTML = "Please wait while the Approval workflow starts on the selected items";
                op.Begin();

                using (var manager = SPContext.Current.Site.WorkflowManager)
                {                    
                    foreach (string id in ids)
                    {
                        //item on which the workflow should run
                        SPListItem item = list.Items.GetItemById(Int32.Parse(id));

                        //start workflow and pass anything you want to it, or in this case null
                        manager.StartWorkflow(item, association, association.AssociationData);
                    }
                }

                //End Long operation
                op.End(list.DefaultViewUrl);
            }
            SPContext.Current.Web.AllowUnsafeUpdates = false;                
        }

Since initiating a workflow on multiple list items could potentially take a long time to complete, we use the SPLongOperation class to delimit that operation. As a result, the user sees the following familiar page that SharePoint always pulls up whenever a long running operation is in progress.

image

The end result is that the Approval workflow automatically starts running on all the selected items

image

%d bloggers like this: