Thursday, September 6, 2012

How to build a jQuery world clock in SharePoint

Update - 9/12/12

This post was deemed worthy of publishing on Nothing But SharePoint's site. May the SEO battle begin (it's a hobby to watch Google's relevancy algorithm in action).

Requirements

On a recent project, my client asked me to produce a world clock web part on his portal’s home page.  He requested a list of office locations and the time in that location, honoring Daylight Savings Time rules if applicable (timeanddate.com’s world clock page is a good working example).  The following screenshot is an example of the layout requested.

world clock web part

My first reaction was, “there must be an existing world clock web part I can use, because how would it be possible that something this common isn’t already out there?”.  So after searching for “sharepoint world clock”, I downloaded and installed the 4 most popular solutions (all server-side web parts), both community and commercial. I was shocked that none of them had this capability out of the box.  Some would have this functionality embedded with weather features, and some would output html that required serious DOM manipulation to extract the data and place it into a tabular output like I needed. When I found one that looked like a close match, there was always something that caused it not to fit, such as the lack of support for the exact time format that I needed, or not being able to override the location name after executing a web service call. Coming to the realization that I was going to have to do something custom, I set out to write my own web part because it seems simple enough, right? :)

After a bit of reflection, I arrived at my initial list of requirements:

  1. No external web service dependencies.
  2. Powered by JavaScript using currently installed web parts.  Try to avoid deploying a solution with compiled code.
  3. Clocks should be based on a more reliable time source than the time on a client machine.
  4. Customized clock format (date and time).
  5. Adjusts for Daylight Savings Time when observed.

Storing the Data

First of all, the question may be raised here as to why we’d want to store this data ourselves.  Won’t this make the data out of date as soon as a locality updates their Daylight Savings Time or decides, gasp, to change their GMT offset one day? In my humble opinion, the rate of change of this data is low enough to justify caching it in a list somewhere on your SharePoint farm. When you combine this with the fact that using an external web service API to fetch this information dynamically would come with a fee attached, it becomes a little easier to justify the infrequent edits required to keep your local storage up to date with the time zones of the world. (If you happen to be that edge case which produces a maintenance burden updating cached time zone and DST data, perhaps consider automating the import of the time zone database?)

So if you’re still with me, let’s do this. The first order of business is to build out the data source.  In order to store the data required to render clocks, I created a list in SharePoint to hold GMT offsets and Daylight Savings Time date ranges.  If you’d like, you can download my list template to get started with your list.  My list has the following design.

world clock list columns

I have to admit I was on the fence regarding the use of separate columns for storing date components of the Daylight Savings Time data (so I therefore reserve the right to change my mind later). The primary reason for this is because SharePoint adjusts date fields based on local time zones, even though it actually stores the date as UTC/GMT in the content database. What would be really cool here is a custom field type in SharePoint called “UTC Date” that doesn’t try do any time zone adjustments. Then, you could just use that for UI date validation and ignore the year upon retrieval when checking for Daylight Savings Time. I’ve built some custom fields types that were more involved than this, so it wouldn’t be too significant an effort, but let’s just stick with what SharePoint gives us to work with and try to keep all of our customizations away from custom Visual Studio solutions, shall we?

Retrieving the Data

Creating the data viewTo make sure I can completely control the HTML output of the list data, I used the Data Form Web Part (also named data view). I prefer these over the default XSLT List View Web Part because of their concise XSLT templates. However, you can read more about the differences on Microsoft’s comparison page. To add the web part, you’ll need to use SharePoint Designer.  On a web part page, select a web part zone and choose Insert, then Data View, then Empty Data View. 

After choosing my World Clock list as the data source, I just added the Title field using the Multiple Item View menu choice to initialize the data view with the proper data source.

Initializing a Data Form Web Part

Don’t worry about customizing anything on the data view at this point (unless it’s to disable paging), because we’re about to rock its world with a shiny new list of parameter bindings and a new XSLT stylesheet.

JSON FTW or: How I Learned to Stop Worrying and Love jQuery

At this point, you should switch over to the Code view so you can begin overwriting all the “less than ideal” XSLT that comes out of the box. First, let’s set up a couple of custom parameters.  In the web part’s markup, find the parameterbindings xml element, and add the following two parameter bindings.  While you’re at it, I’d recommend removing all the parameters except ListID. It tends to reappear even if you delete it, even though we don’t really need the List ID in the stylesheet. Besides, it’s already embedded in the data source, which is where it counts.

<ParameterBinding Name="Title" Location="WPProperty(Title)"/>
<ParameterBinding Name="DateTimeFormat" Location="None" DefaultValue="ddd h:mm A"/>

The Title parameter passes in the web part’s title to the clock so we can more easily control styling without having to be concerned with attempting to style the normal chrome header text (just set the Chrome to None and we’re good). However, if you’ve already customized the styling of your chrome, then have at it.  This is definitely an optional step that I decided to do.

The DateTimeFormat parameter is a format string for displaying the time. In this example, I’m using a format string compatible with the moment.js library. As you’ll see later on, however, this solution doesn’t require you to use moment.js, so you can plug in any JavaScript date library you may already be using. I decided to make the format string a parameter so it could be easily edited on the fly without requiring a new version of your js file to be published. This also means someone not very comfortable with JavaScript could update it with a lot less intimidation.

In order to pass data to jQuery, I’m going to take a separation of concerns approach to XSLT, making it responsible for as little as I possibly can, which some (of the “2 problems” persuasion) would say is the proper way to treat this technology.  Given SharePoint’s current limitation of only supporting XSLT 1.0, we have very little in the category of useful functions to help us out here anyway. Plus, XSLT has the tendency to be bloated and hard to follow, becoming a potential long-term maintenance concern. 

The solution? JSON FTW!  The following stylesheet transforms the List data XML into inline JSON that jQuery will then be able to deserialize easily.  In this example, I’ve tried to remove absolutely everything out of the default stylesheet that I could get away with. For readability, I’ve left quotation marks in the JSON templates.  Although these will be replaced with &quot; after you save and close, it won’t affect the output.  That’s the not the only thing that will be modified after saving and closing, however. See those unused ddwrt namespaces?  Try and delete them and they will re-inserted back into your stylesheet after you close and reopen your page in SharePoint Designer.

<Xsl>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:ddwrt2="urn:frontpage:internal">
    
    <xsl:output method="html" indent="no"/>

    <xsl:param name="Title" />
    <xsl:param name="DateTimeFormat" />
    
    <xsl:template match="/"
        xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
        <xsl:call-template name="clocks" />    
    </xsl:template>                                
                            
    <xsl:template name="clocks">
        <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
        
        <xsl:if test="count($Rows) &gt; 0">            
            <div id="worldClock"> 
                <div class="config">
                    <xsl:call-template name="configJSON" />            
                </div>
                <div class="header">
                    <xsl:value-of select="$Title" />
                </div>
                <div class="clocks">
                    <table>
                        <xsl:for-each select="$Rows">
                            <tr>
                                <td>
                                    <div class="title">
                                        <xsl:value-of select="@Title"/>
                                    </div>
                                </td>
                                <td>                                
                                    <div class="clock">    
                                        <xsl:call-template name="clockJSON">
                                            <xsl:with-param name="GMTOffset" select="@GMTOffset" />
                                            <xsl:with-param name="DSTGMTOffset" select="@DSTGMTOffset" />
                                            <xsl:with-param name="DSTBeginMonth" select="@DSTBeginMonth" />
                                            <xsl:with-param name="DSTBeginDay" select="@DSTBeginDay" />
                                            <xsl:with-param name="DSTBeginHour" select="@DSTBeginHour" />
                                            <xsl:with-param name="DSTBeginMinute" select="@DSTBeginMinute" />
                                            <xsl:with-param name="DSTEndMonth" select="@DSTEndMonth" />
                                            <xsl:with-param name="DSTEndDay" select="@DSTEndDay" />
                                            <xsl:with-param name="DSTEndHour" select="@DSTEndHour" />
                                            <xsl:with-param name="DSTEndMinute" select="@DSTEndMinute" />
                                        </xsl:call-template>    
                                    </div>
                                </td>
                            </tr>                        
                        </xsl:for-each>                    
                    </table>
                </div>
            </div>
        </xsl:if>    
    </xsl:template>
    
    <xsl:template name="configJSON">
        {
            "formatString" : "<xsl:value-of select="$DateTimeFormat" />"
        }
    </xsl:template>
    
    <xsl:template name="clockJSON">
        <xsl:param name="GMTOffset" />
        <xsl:param name="DSTGMTOffset" />
        <xsl:param name="DSTBeginMonth" />
        <xsl:param name="DSTBeginDay" />
        <xsl:param name="DSTBeginHour" />
        <xsl:param name="DSTBeginMinute" />
        <xsl:param name="DSTEndMonth" />
        <xsl:param name="DSTEndDay" />
        <xsl:param name="DSTEndHour" />
        <xsl:param name="DSTEndMinute" />                                                        
        {
        "gmtOffset": <xsl:value-of select="$GMTOffset" />
        <xsl:if test="string-length($DSTGMTOffset) &gt; 0
                  and string-length($DSTBeginMonth) &gt; 0
                  and string-length($DSTBeginDay) &gt; 0
                  and string-length($DSTBeginHour) &gt; 0
                  and string-length($DSTBeginMinute) &gt; 0
                  and string-length($DSTEndMonth) &gt; 0
                  and string-length($DSTEndDay) &gt; 0
                  and string-length($DSTEndHour) &gt; 0
                  and string-length($DSTEndMinute) &gt; 0">,
        "dstGmtOffset": <xsl:value-of select="$DSTGMTOffset" />,
        "dstBeginMonth": <xsl:value-of select="$DSTBeginMonth" />,
        "dstBeginDay": <xsl:value-of select="$DSTBeginDay" />,
        "dstBeginHour": <xsl:value-of select="$DSTBeginHour" />,
        "dstBeginMinute": <xsl:value-of select="$DSTBeginMinute" />,
        "dstEndMonth": <xsl:value-of select="$DSTEndMonth" />,
        "dstEndDay": <xsl:value-of select="$DSTEndDay" />,
        "dstEndHour": <xsl:value-of select="$DSTEndHour" />,
        "dstEndMinute": <xsl:value-of select="$DSTEndMinute" />
        </xsl:if>
        }
    </xsl:template>

</xsl:stylesheet>
</Xsl>

Styling the output

At this point, you should see something like this in your output:

image

Since jQuery will wait for the page to load before it will begins processing the JSON, it would probably be a good idea to hide the JSON by default.  The following CSS is one example of how we might do this.  Setting the JSON elements’ CSS style to display: none is equivalent to how jQuery implements its hide() method.  Therefore, whenever we’re ready to display the content after rendering, it will be as easy as calling show().  Also, in this example I’ve set the width of the containing table to 100%, so the web part will expand to fit whatever zone you place it in, or you can simply set the width in the web part properties.

/* world clock styles */
#worldClock {
    font-size: 8pt;
    width:100%;
}

#worldClock .config {
    display:none; /* always hide config JSON */ 
}

#worldClock .header {    
    text-align: center;    
    padding: 4px;
    font-size: 12pt;
    font-weight:bold;
}

#worldClock .clocks {
    padding: 4px;
}

#worldClock table, #worldClock td {
    width: 100%;
}

#worldClock .clock {
    display: none; /* hides JSON during rendering */
    text-align: right;
    white-space: nowrap;
}

Rendering the Clocks Using OO JavaScript

Now that we’ve swept all that JSON under the CSS rug, let’s get to the fun stuff: the code. What we need to do is basically iterate over each clock and replace the JSON with a formatted time.  Sounds easy, right?  Before you start ROTFLOL too soon, it’s actually a lot easier and more concise than perhaps what you’ve seen in the past when stumbling across world clock scripts that work with Daylight Savings Time. Following is all the JavaScript you’ll need in order to do this in an object-oriented fashion.

(function(){ // IIFEs protect your globals 

    $(function(){
        
        var config = $.parseJSON($("#worldClock .config").text());    
        
        // using moment.js for parsing and formatting functions
        // replace with your favorite date library if desired
        
        var parseFn = function(httpDateHeader) {
            var gmtMoment = moment.utc(httpDateHeader);
            return new Date(gmtMoment.year(), 
                            gmtMoment.month(), 
                            gmtMoment.date(), 
                            gmtMoment.hours(), 
                            gmtMoment.minutes());
        };
    
        var formatFn = function(date) {
            return moment(date).format(config.formatString);
        };
        
        var gmt = getGmtFromServer(parseFn);
        
        $("#worldClock .clock").each(function() {    
            var json = $(this).text();        
            var data = $.parseJSON(json);
            
            var clock = new Clock(data, gmt, formatFn);
                            
            $(this).text(clock.toString());
            $(this).show();
        });
    });                
        
    // issues HEAD request to the page to get date header
    // parseFn: date parse function for the RFC 822 date header
    function getGmtFromServer(parseFn) {
        var gmt;
        
        $.ajax({
            url: location.href,
            async: false,
            type: "HEAD",
            success: function(data, textStatus, jqXHR){
                gmt = parseFn(jqXHR.getResponseHeader("Date"));                            
            }
        });
        
        return gmt;
    }

    // Clock constructor
    function Clock(data, gmt, formatFn) {        
        // copy properties from deserialized JSON object
        for (prop in data)
            if (data.hasOwnProperty(prop)) this[prop] = data[prop];
        
        this.gmt = gmt;
        this.formatFn = formatFn;
    }
    
    Clock.prototype = {
        getDSTBegin : function() {
                return new Date(this.gmt.getUTCFullYear(), 
                    this.dstBeginMonth - 1, 
                    this.dstBeginDay, 
                    this.dstBeginHour, 
                    this.dstBeginMinute, 
                    0);
            },
                    
        getDSTEnd : function() {
                return new Date(this.gmt.getUTCFullYear(), 
                    this.dstEndMonth - 1, 
                    this.dstEndDay, 
                    this.dstEndHour, 
                    this.dstEndMinute, 
                    0);
            },    
    
        toStandardTime : function() {
                return (this.gmtOffset === 0)
                    ? this.gmt
                    : this.gmt.addHours(this.gmtOffset);
            },
    
        toDaylightSavingsTime : function() {
                return (this.dstGmtOffset === 0)
                    ? this.gmt
                    : this.gmt.addHours(this.dstGmtOffset);
            },
    
        toString : function(){
            var time = this.toStandardTime(); 
        
            if(typeof this.dstGmtOffset !== "undefined")
            {
                var daylightSavingsTime = this.toDaylightSavingsTime();
                
                var dstBegin = this.getDSTBegin();        
                var dstEnd = this.getDSTEnd();
        
                if(time >= dstBegin && daylightSavingsTime < dstEnd)
                    time = daylightSavingsTime;
            }
                    
            return this.formatFn(time); 
        }
    };
    
    // adds hours to a Date instance
    Date.prototype.addHours = function(hours) {
        return new Date(this.getTime() + hours * 60 * 60 * 1000);    
    };

})();

Finding a reliable GMT (They don’t make them like they used to)

Now that I’ve shown you all my cards, allow me to explain myself a bit. One of the first items for discussion is how to reliably determine the current GMT. You know you’re going about it the wrong way if you see the following line of code anywhere in your solution, which indicates you’re relying on the current time on the client’s machine (and you have no idea where that thing has been).

var date = new Date(); // client-side implementation FAIL! 

My solution was to use the time on the web server, the assumption being that the time on the server is correct.  Some may argue that this is not reliable enough, but if the clocks on your servers are off, you may likely have more significant problems (Kerberos *cough cough*) to be concerned with than just your world clock web part showing the wrong time.

Take a quick look at the getGmtFromServer() function above. This issues a HEAD AJAX call right back to the hosting page for the purposes of fetching the HTTP Date header, which just so happens to be the most convenient way to get the current time in GMT from SharePoint.  All the XSLT date functions available to us return the local time on the server, but I found no easy way of determining the server’s time zone.  Instead of making this task a much more complicated ordeal, I punted to IIS for the information I needed from the good old HTTP specification.

By the way, if you were ever searching for a good use case for the HEAD method, I would submit this one as highly appropriate. Why issue a full-blown GET request and bother the server to do all that resource rendering when you can just tell it to return what we need, which is just the headers. Unfortunately we do have to make an AJAX request to get the headers, since the browsers don’t currently trouble themselves to store the HTTP response headers anywhere after a page loads. Also, the AJAX request is a little unorthodox because I forced it to be synchronous, but I think it’s appropriate in this case (it’s an extremely lightweight request and the code is a little easier on the eyes).

Date parsing and formatting

While I’m on the topic of dates, this would be a good time to discuss the ready function.  Since JavaScript doesn’t offer extensive date parsing or formatting out of the box, you’re basically on your own to do this yourself. Since this has been a problem for a long time, by now there are probably at least a dozen different date libraries to choose from.  I initially attempted to use date.js, but for whatever reason it no longer worked correctly (It formatted midnight as a 0 instead of 12).  I then searched and found moment.js, an awesome date library that’s at least 4 years younger than date.js and smaller in size as well.  It’s capabilities far exceed my requirements and it’s available on a CDN. Winning!

Even though I may be excited about moment.js right now, maybe I’ll change my mind later, or perhaps even find some shinier date library.  This thought inspired me to write the 2 functions at the top of the ready function: parseFn for parsing the RFC 822 date header and formatFn for rendering the formatted time output. You can (and most likely will) change these functions to parse and format however you’d like without affecting how the world clock works.

Nerdy details for those so inclined to be educated (here there be dragons)

Defining functions and passing them as parameters keeps the world clock ignorant of however you decide to parse and format dates.  This is an algorithmic dependency injection technique called the strategy pattern. And as a bonus teachable moment, parseFn creates a closure by exposing the config object. The closure is how the immediate scope of Clock.toString understands what config.formatString is when it calls parseFn.

Conclusion

I’ve made every effort to make the JavaScript as readable and self-documenting as possible. I even sprinkled in some additional comments just in case you see something that may appear foreign at first glance (such as IIFEs and constructor functions). Even though this example represents a very specific set of requirements, I can imagine how using the XSLT to JSON to jQuery (XJQ?) technique could be quite effective in a lot of use cases. 

I hope you’ve found this implementation a useful example on how to integrate jQuery with SharePoint. Think of it as “a way” to get JSON to jQuery, but not “the way”, as we do have other options available, such as the OData feed.

Oh yeah, and I almost forgot to tell you the specific versions of what I was running.

  • SharePoint 2010
  • jQuery 1.8.1
  • moment.js 1.7.0

Happy SharePointing!

Monday, August 27, 2012

Yes, you can do recursion in a Nintex workflow

Requirements

I was recently introduced to the Nintex Workflow 2010 product several months ago, and during construction of one of my workflows for a client, I found myself needing recursion.  Why, you ask?  Well, this problem always seems to surface whenever a hierarchy is involved, such as folders in a file system that have subfolders. In my case, I had a list in SharePoint that contained a template of tasks that were to occur in a certain order, and each task had a relationship to another task.  For example, “Set up utilities” must occur 30 days before from “Move in to my new house”.  What I knew ahead of time was only “Move-in Date”, and I wanted my workflow to basically walk through the hierarchy of related tasks and calculate dates for all of my tasks.

If I were coding this in a programming language, I’d just create a recursive method that called itself with the base date and passed that to all related tasks so they could do their calculations.  However, in order to hand this calculation procedure over to a client that had no coders on staff, Nintex Workflow was the chosen vehicle for delivery. When I first used Nintex, I didn’t easily spot a construct that would allow me to implement recursion. Sure, workflows can call other workflows, but the number of simultaneous executing workflows could get out of hand causing SharePoint to rain on that parade rather quickly.

Prerequisite Knowledge

It’s helpful at this point to step back a bit and think about what we’re trying to do. The feature I want in Nintex is basically the ability to call a function (method is more of an object oriented programming label), but Nintex doesn’t provide the concept of a function. But what is a function, and how does recursion within functions normally work in a standard programming environment you ask? (you ask a lot of questions, but that’s a good thing)

Computer Science 101 and 201

When I was in college getting my bachelor’s degree in Computer Science, there were a couple courses I took that proved helpful to me when determining how to set this up.  The first is Assembly Programming 101, where one learns exactly what happens behind the scenes when a function is called.  The short answer is to use a data structure called a stack, which I learned in another class, Data Structures 101. In assembly, when you want to call a function that requires a parameter, you “push” the parameter (using a register to store the value) onto the stack first.  Then, once inside the function, you “pop” the value off the stack to use it.  So, the next time you’re writing methods in C#, just think about all those stack operations going on behind the scenes on your behalf.  :)

Stack data structure animation
Helpful animation explaining stacks

So, now that you understand how a function call might work with a stack, how does recursion work?  Instead of scaring you away with terms like “depth-first preorder tree traversal” (also from Data Structures 101), just know that we’ll need to keep a running list of tasks that have related tasks, and we’ll use a stack for that.

Here’s the algorithm:

  1. Every time you find a task (parent) that has related tasks (children), push the parent to the stack.
  2. Loop over the stack, pop an item off, and get children.
  3. Repeat step 1 and 2 until the list is empty.

 

Preorder tree traversal animation
Helpful animations of tree traversals

See, easy as pie, right?

Nintex Workflow 2010 Tools

Now that we’re familiar with the building blocks of what we’ll need, let’s take a look of what Nintex does provide us with to see if we can implement our algorithm.

A stack can be implemented in Nintex as a collection variable.  Collection variables in Nintex are ordered 1-dimensional arrays (for the programmers out there).  You cannot store something like an object with properties in a collection variable.  If you need a collection of objects, you’d just use multiple collections and load them in the same order (they remain ordered in the way they’re loaded).  Then, for example, you can trust the 3rd item in each collection represents the properties of the 3rd object you loaded.
Nintex collection variable

Looping can be done with using either a Loop action or a For Each action. Nintex looping actions

  • The Loop action simply loops until a condition is met that causes it to stop. 
  • The For Each action loops over the contents of a collection variable, but also has a condition property that will cause it to stop processing. When the loop is first entered, a snapshot of the collection is created and used for the duration of the iterations. As you’ll see in the implementation below, this disqualifies this action from being used against a stack type of collection variable, since any changes we make to the collection during a loop are not detected by the For Each action. 

Implementation

Summary

The screenshot below is high-level example of how the workflow needs to be structured.  Following is an overview of what’s going on.

Example Nintex recursion workflow

  1. Push a baseline task to the stack to initialize it.
  2. Loop over the stack. A For Each action cannot be used against the stack collection since the collection is changed inside the loop (pushing and popping).
  3. Process the task.  This includes popping the item(s) off of the stack and fetching the related tasks into a set of collections. As stated previously, a collection per column is required if you want to loop over a set of list items (or “records” or “objects”).
  4. Loop over the related tasks.  This can be a For Each action, since the collections that hold related task data will not change during the loop.  In this loop, finally I can do our date calculations and create the tasks.  A search is done against each item to determine if related tasks exist.  If so, you add it to the stack.
  5. Recalculate the size of the stack and repeat step 2 until the stack is empty.

 

Nintex Workflow Variables
Variable Type Description
Stack Collection Stack for fetching related tasks.
Date Stack Collection Stack for passing the date as a parameter for calculations.
Baseline Id Single line of text Stores the ID of the starting task. The first value in the Stack collection.
Stack Count Integer Used by outer loop to determine to finish looping. Used by Collection operation action to count the Stack collection. Initialized to 1.
Related Id Single line of text Used to pop the top item off of the Stack.
Related Date Date and Time Used to pop the top item off of the Date Stack.
index Number Used for synchronizing the retrieval of column data across related task collections.
Id Collection Collection Used by query action to store the ID column for related tasks.
Title Collection Collection Used by query action to store the Title column for related tasks.
Days Collection Collection Used by query action to store the "Days from Related Task" column for related tasks.
Id Single line of text Used by Collection operation action to retrieve the ID column from the Id collection.
Title Single line of text Used by Collection operation action to retrieve the Title column from the Title collection.
Days Number Used by Collection operation action to retrieve the "Days from Related Task" column from the Days collection.
Date Date and Time Stores the result of the date calculation for a related task.
Related Count Single line of text Stores the result of a query to get the count of related tasks. This is text instead of a number to avoid unnecessary type casting since I'm using the OData feed and a Web request action. Therefore, if this variable has anything besides "0", the current ID and Date are pushed to the stacks.
Sample list definition
Column Type Description
Title Single line of text  
Related Task Lookup Lookup column back to this list
Days From Related Task Number  
Sample list data
Title Related Task Days From Related Task
Gather documents for closing Closing -30
Closing Move In -5
Set up utilities Move In -30
Move In    
Details

For further details, let’s dive in a little deeper into each of the steps summarized above.

  1. In order to “push” items into our stack, I’m simply adding items to the collection.  This is achieved through the use of a Collection operation action using the Add option.
      Pushing to the stack
  2. Loop over the stack while the count of items in the stack collection is greater than 0. This is achieved using a variable, Stack Count, and evaluating against this in the Loop action.
    Stack loop condition
  3. Process the task. In order to pop items off of the stack, you can again use the Collection operation action with the Pop option as shown in the following screenshot.
    Popping from the stack
    Once I have the Related Id, I can issue a query against our list to get all related tasks.  This can be achieved using either a Query list action or a Query XML action against the OData list data feed. To store the related tasks, you’ll create a collection to hold each column that you’re concerned with. For my purposes, I’ll need at least the ID, Title and “Days From Related Task” columns in order to create my list items. This means I’ll have 3 collections (as shown above in the variables list). 
  4. Loop over the related tasks. This loop action can be a For Each since I’ve already loaded the collections from the previous query (the collections are replaced by the query action in the previous step). This is where the index variable becomes important.  The For Each action will iterate over a collection and store the index (incrementing number starting at 1) in a variable (named appropriately I might say). 
    For Each action properties

    The first actions in the loop fetch the values from the remaining collections based on this index:
    Collection operation actions to fetch remaining properties

    Here’s an example of what the Get Title operation looks like:
    Synchronized Collection operation action

    Now that I have all the column values into variables, I can do my date calculation and create my task.

    At this point I had a choice in how to implement my recursion algorithm.  Either I could check for related tasks now (issue a count aggregate query) or later (let step 3 handle what to do if no related tasks were found from the query actions).  I chose to issue a count query (a special quality and appropriate plug for the OData feed) to determine if the current item’s ID and date get pushed to the stack.  This kept the stack cleaner in my opinion and simplified the retrieval logic in step 3. Pushing items to the stacks are accomplished in an identical manner as in step 1 using a Collection operation action with the Add option.
  5. Recalculate the size of the stack.  This can be done using a Collection operation action with the Count option.
    Counting the stack collection

Final concerns

There are 2 final things that need to be mentioned here to set you up for success: Maintenance and Safe looping.

Maintenance

Implementing an algorithm like this in a workflow is an advanced technique and it’s complicated.  Whenever I’m writing complicated C# code, I try my best to make it as simple and possible for the best long-term maintenance story. There are all sorts of software development techniques I can pull from to accomplish that, but I’ll oversimplify them here in a list and we can see how you can do the same thing in Nintex.

Code Nintex Workflow
Descriptive variable names Descriptive variable names
Descriptive method names Descriptive workflow action names
Refactor - Extract method Descriptive-named action sets
Commenting “User-generated” comments can be added to actions
Logging “Log in history list” action
Unit testing You’re kidding, right?  No one has written the NintexUnit framework to my knowledge. If you want unit testing in a workflow, you’re most likely going to be using a full-blown Visual Studio workflow with .NET and not Nintex.

Based on the table above, following is a list of things to make sure you do in a workflow to make it easier to maintain and support over time.

  • In most cases, rename the workflow action to explain what it’s doing. Please don’t leave the default text there whenever you have more than 1 type of that action in a workflow. If you do a lot of logging in your workflows, rename each logging action to explain what’s inside. This will help you out later if you ever need to delete a variable that may be referenced in them.
  • Use descriptive variable names. You can be even more verbose than you could be in code since you can have spaces in your variable names.
  • You should also add your own comments to the actions whenever there is any doubt as to what a particular action is up to.
  • Add logging actions whenever you think something may go wrong that might require research.  A common thing to do would be to log your variables. 

    One way to do this is to add text and variables to the “Message to log on completion” text box on the action. This helps to reduce the number of logging actions in your workflow.  I normally do this on actions that set variables, as in the screenshot below where I set the Stack Count variable.
    Customized logging within an action
  • Use Action set actions to group related workflow actions whenever possible.  You can then minimize them to make the workflow easier to navigate.
Safe looping

I can no longer avoid mentioning Safe looping, the elephant in the room. Safe looping is a Nintex global setting in Central Admin that makes sure someone doesn’t write an out of control looping algorithm (real easy to do that with the State Machine workflow action) that would bring a web front end server down with excessive load. It does this by inserting a pause of 5 minutes (derived from a timer job interval setting in your SharePoint environment) for each loop iteration during publish time. This means our Loop action will have pauses inserted while we’re “stacking it where they lacking it” (really sorry for that, but I couldn’t resist). This is not good, folks. When I was working with a client on a similar workflow, the difference between having safe looping turned off or on was whether the workflow would finish in 30 seconds or 30 minutes, respectively. Waiting 30 minutes for a workflow was not an acceptable option.

The solution is easier than you think, and you don’t have to sacrifice the safety of your farm in the process, either. All you need to do is make sure that safe looping is turned off when you publish your workflow. Since this setting requires an iisreset to take effect immediately, you could use the following steps to make sure you have a delay-free workflow at run-time.

  1. Turn Safe looping off
  2. Do an iisreset to force the setting to refresh
  3. Publish your workflow
  4. Turn Safe looping back on
  5. Do an iisreset to force the setting to refresh

This is obviously something you’d do outside of normal business hours, since users don’t tend to like the server going down (FAIL!). Since safe looping is enforced by modifying your workflow by injecting pauses during a publish, you can “safely” turn Safe looping back on and it won’t go back and mess with your workflow.  

Is this setting a deal-breaker in your environment? Well, even though Nintex comes out of the box with Safe looping turned on, it is, after all, still a Central Admin setting in the UI, so this is definitely a supported configuration. As long as you exercise caution when overriding the default, it should be viewed like any other executable code you put in production.  Hopefully you have multiple environments where you can do your workflow testing, so there should be a certain level of confidence by the time you’re ready to publish a workflow in a production farm, which is then followed by testing.

In my opinion Nintex workflows are supposed to help us solve a problem quickly, but I think this setting often gets directly in the way of that. Perhaps we’ll see some enhancements in the Nintex platform in the future to better support something like the steps I listed above, but it’s quite possible it won’t happen given the general direction of SharePoint 2013 moving away from web front ends being responsible for executing workflows. 

Conclusion

It’s likely that your implementation of recursion will have nothing to do with a self-referencing lookup column, in which case the fashion in which you control how items get pushed and popped from your stacks would be completely different.  Whatever the use case, I hope you see from my example how you could achieve recursion walking any tree of related items.

And, last but not least, I’ve exported a site workflow that you can use to inspect the design a little closer, or even use as a starting point for your recursion needs.  I wrapped the whole workflow in an action set so you can save it as a snippet and move it to any other type (such as a reusable template or list workflow).

Cheers!

Sunday, June 3, 2012

Ext Js 4 presentation at VNext Dallas

I had the privilege of presenting a couple months ago at the VNext Dallas user group on my experiences with Ext Js 4 during a recent engagement at a client.  The slide deck is embedded below.  If you’d like a little more context than what you can get in the slides (I make the slides like this for a reason, you know), I would point you towards the video of the presentation on UserGroup.tv, the guys that make the existence of the video possible.

I’d like to thank Shawn Weisfeld especially for recording this session, and for always showing up at the user group meetings no matter the size!  His dedication to the community is unlike anything I’ve seen, folks.




Friday, February 18, 2011

“Prism is not a MVVM framework!” and other goodness from the MVVM Smackdown

A couple months ago I participated in a friendly “smackdown” of MVVM frameworks at the North Texas Silverlight User Group.  To sum up the exchange, I represented the Prism offering from the Microsoft Patterns and Practices group (which is awesome by the way for composite applications), Dave Yancey presented Jounce, Bonas Khanal presented MVVM Light, and Justin Weinberg presented Caliburn Micro. If you want more information about my provocative post title, you’ll need to get the context from my video in the list below.