Display All Related Tasks for a SharePoint Workflow Using jQuery, SPServices, and jQueryUI

This was a fun one to build. A client wanted to be able to see all of the related tasks for a workflow without having to do the two or more clicks it can take to get there using a standard SharePoint list view. By layering jQuery, SPServices, and jQueryUI, we were able to display the information in a nicely formatted dialog with only one click, and make it a richer experience, to boot!

Using Christophe Humbert’s (@Path2SharePoint) excellent Easy Tabs, each user had their own customized dashboard which showed their current workload. This dashboard was part of a rich user experience which was really a one-stop shop for each user to accomplish all of their main SharePoint tasks. Each tab had a straightforward view of a Document Library which showed the current status of the workflow for each document., as shown below.

image

The column on the far right is a status column for the Customer Request workflow. As you can see, while the regular text link to details about the workflow is available, there’s also a small question mark next to the documents where the workflow is still running.

When the user clicks on the question mark for any document, they get a concise view of the task details for that workflow instance:

image

I built the script so that it will show all of the tasks for the document if there is more than one:

image

If you think through the normal steps to see those details, it’s at least two or three clicks to get to the workflow’s tasks, and the two or three to get back where you were. This was the goal: to provide a single click way to understand the current workflow status. (We considered showing the details on the hover event, but it seemed as though that would be a bit too obtrusive.)

So, how did all of this work? Well first I added the appropriate references to the page. In this case, I added the following lines below the line:

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<script language="javascript" type="text/javascript" src="/Scripts/jquery-1.5.min.js"></script>
<script language="javascript" type="text/javascript" src="/Scripts/jquery.SPServices-0.6.1.min.js"></script>
<script language="javascript" type="text/javascript" src="/Scripts/jquery-ui-1.8.12.custom.min.js"></script>
<script language="javascript" type="text/javascript" src="/Scripts/Workflow.js"></script>
<link rel="stylesheet" href="/Scripts/css/jquery-ui-1.8.12.custom.css"/>
<link rel="stylesheet" href="/Scripts/css/Workflow.css"/>

We needed the jQuery library, my SPServices library, the jQueryUI library, my custom script for the page, the jQueryUI CSS for the selected theme, and my custom CSS.

The custom CSS ended being pretty insignificant; it just made a couple of small changes to one class.

.cm-app-info {
    display:inline-block;
    cursor:pointer;
}

The meat of things was Workflow.js. In that script file, I did all the work to make the modal dialog happen. I’ve added comments throughout the script, so hopefully it will make decent sense. I’ve cut out a few extraneous bits to simplify things, so I hope that I haven’t introduced any errors. In any case, you should always consider code samples in my blog (and most others) simply as a starting point for doing your own thing.

$(document).ready(function() {

 // In this section, we find the important elements in the DOM for later use
 // --> Note that the specifics in the selectors will be different in your situation, but you can use this as a model
 var customerContractsTable = $("table#onetidDoclibViewTbl0[summary='Customer Contracts ']");
 var customerContracts = $("table#onetidDoclibViewTbl0[summary='Customer Contracts '] > tbody > tr:gt(0)");

 // Add a div to hold the text for the modal dialog
 $(customerContractsTable).after("<div id='workflowDetailsText'></div>");
 var workflowDetailsText = $("#workflowDetailsText");

 // Find each Customer Requests cell (the seventh column, which with zero-indexing is in position 6) where the current status = "2" (In Progress)
 $(customerContracts).find("td:eq(6) span[value='2']").each(function() {
  // For each, show the question mark icon from jQueryUI by appending a span with the appropriate CSS classes
  $(this).closest("td").append("<span class='cm-app-info ui-icon ui-icon-help' ></span>");
 });

 // When the user clicks on the question mark icon...
 $(".cm-app-info").click(function () {

  // Get the link to the current item (servername hard coded for simplicity - not best practice)
  var thisItem = "https://servername" + $(this).closest("tr").find("td.ms-vb-title a").attr("href");

  // Call the Workflow Web Service (GetWorkflowDataForItem operation) to get the currently running workflows for this item
  $().SPServices({
   operation: "GetWorkflowDataForItem",
   item: thisItem,
   async: false, // We'll do this asynchronously
   completefunc: function (xData, Status) {

    $(xData.responseXML).find("ActiveWorkflowsData > Workflows > Workflow").each(function() {
     var thisStatus = $(this).attr("Status1");

     // Only show workflows which are currently In Progress (Status1 = 2)
     if(thisStatus == "2") {
      var thisTaskListId = $(this).attr("TaskListId");
      var thisTemplateId = $(this).attr("TemplateId");

      // With the information we've just gotten for the workflow instance, get the relevent task items in the Workflow Tasks List
      $().SPServices({
       operation: "GetListItems",
       listName: thisTaskListId,
       async: false, // We'll do this asynchronously
       completefunc: function (xData, Status) {

        // Initiation
        $(workflowDetailsText).html("");
        var out = "<table>";
        var taskNum = 0;

        // Get information for each of the task items
        $(xData.responseXML).find("[nodeName='z:row']").each(function() {
         if($(this).attr("ows_WorkflowLink").indexOf(thisItem) > -1) {

          taskNum++;

          // Format Assigned To
          var assignedTo = $(this).attr("ows_AssignedTo");
          assignedTo = "<a href='/_layouts/userdisp.aspx?ID=" + assignedTo.substring(0, assignedTo.search(";#")) +
           "&Source=" + location.href + "'>" +
           assignedTo.substring(assignedTo.search(";#") + 2) + "</a>";

          // Format Due Date
          var dueDate = $(this).attr("ows_DueDate");
          dueDate =
           dueDate.substr(5, 2) + "/" + // month
           dueDate.substr(8, 2) + "/" + // day
           dueDate.substr(0, 4); // year

          // Format Percent Complete
          var percentComplete = 100 * parseFloat($(this).attr("ows_PercentComplete")).toFixed(0);

          // Add the information to the dialog
          out += "<tr><td>Task #" + taskNum + ":</td><td>" + $(this).attr("ows_Title") + "</td></tr>" +
           "<tr><td>Assigned to:</td><td>" + assignedTo + "</td></tr>" +
           "<tr><td>Priority:</td><td>" + $(this).attr("ows_Priority") + "</td></tr>" +
           "<tr><td>Complete:</td><td>" + percentComplete + "%</td></tr>" +
           "<tr><td>Due:</td><td>" + dueDate + "</td></tr>" +
           "<tr><td colspan='99'><hr/></td></tr>";
         };
        });
        out += "</table>";

        // Add the assembled markup to the container we built for it
        $(workflowDetailsText).html(out);

        // Show the dialog using jQueryUI's .dialog() function
        $(workflowDetailsText).dialog({
         open: true,
         title: "Open Tasks",
         minHeight: 500,
         minWidth: 500,
         modal: true,
         buttons: {
          OK: function() {
           $(this).dialog( "close" );
          }
         }
        });
       }
      });
     }
    });
   }
  });
 });
});

I think that this is a really nice use of scripting to enhance the user experience. Not only does the page start to feel more like a “modern” Web page, but we significantly reduced the number of clicks the user would need to do to accomplish their task. Truth be told, even if they did know what click route to follow, the display of the workflow status information is fixed and not all that intuitive. By using this scripting approach, we can easily change how we display the information in the modal dialog. One enhancement we considered was to grab the photo from the User Profile for the person who currently owned the workflow task. This would be a nice little add on.

One other thing worth noting: because the script is built to do the Web Services calls only if the user clicks on one of the icons, there’s little overhead in those instances where the user doesn’t choose to click. There’s no postback, and only the information from the list items which are required to display the full status is requested.

23 Comments

  1. This is great! Usually, it takes me far too long to figure out how to notify the next person in the chain that I’ve done my task and the document is all set for him–let alone see what others have done before me or will do after. You’d think workflows would just work, but a lot of people here just use good ole email instead.

    Reply
  2. REALLY nice solution here, Marc. One of my team’s goals in supporting our department is to continuously make processes easier for employees. Stuff like this goes a LONG way toward making SharePoint the preferred platform for BPM.

    P.S. I’m trying out the new commenting feature using Twitter. Pretty nice stuff!

    Reply
    • Clare and Josh:

      Glad you like! This is the type of relatively easy (it took me about a day total) enhancement that makes a world of different to a good ole user. And aren’t they the whole point?

      M.

      Reply
  3. Hi Mark,

    I have a scenario where in I am working on tasks list and have Comments column which display comment made by user.

    What I am trying to achieve is whenever user create comment it should be display in data view webpart with date and time comment created and Comment itself.

    right now its displays all the comments made at different time together and not displaying date time also.

    could you please give any hint on this?

    Reply
    • You’ll need the jQuery and jQueryUI .js files, which are available at their two sites, and then everything else ought to be here in the post.

      You will definitely need to adapt the script; this is an example, not something that is plug and play.

      M.

      Reply
  4. Hi Marc,

    Any good examples out there to dynamically display User Profile Department from a people picker column in a list view? That is, Department is not a column on the list, but displays for a specific people column.

    Thanks!

    Reply
    • Erik:

      As with most of the stuff you find on my blog, you should consider this an interesting example, not a reusable solution. What made sense in this case might not in yours.

      M.

      Reply
  5. Marc,

    I appreciate the quick reply. Any suggestions on how I can just display the tasks related to the specific list item workflow? Are there any other ows fields I can key on?

    Reply
    • Erik:

      The available fields will vary based on how your list is set up. you should take a look at what is returned from GetListItems with no filters on anything to see what you have to work with.

      M.

      Reply
  6. Marc,

    Is there a way to show participants of the workflow before they are assigned a task? Scenerio a serial workflow is assigned to 4 participants (A, B, C & D). I want to show all tasks, even if they haven’t been assigned yet.

    That way you can see all approvers that need to act on the workflow, even if they aren’t assigned yet.

    Thanks,
    TD

    Reply

Have a thought or opinion?