SPServices Stories #10 – jqGrid Implementation Using SPServices in SharePoint


Today’s SPServices Story comes from Prateek Kulkarni in Bengaluru, India. Prateek posted this originally on the C# Corner site as jqGrid Implementation Using SpServices in SharePoint.

As with several of the earlier SPServices Stories posts, I found this one interesting because it shows how to use SPServices with another framework to render the results obtained from the Web Services calls.

I’ve had several people tell me that there isn’t enough “story” to these SPServices Stories. Have a better one? Let me know!

jqGrid with SPServices

This article is regarding implementation of jqGrid(demo) using SPService(CodePlex)

SPService is a jQuery library which abstracts SharePoint’s Web Services and makes them easier to use. It also includes functions which use the various Web Service operations to provide more useful (and cool) capabilities. It works entirely client side and requires no server installation.

Use of SPService and jQuery is best explained at Marc’s blog.

Following Js files are needed for JqGrid Implementation with SPService

  • jquery-1.8.2.js
  • grid.locale-en.js
  • jquery.jqGrid.min.js  // Structuring jqGrid
  • json2-min.js  // Parsing data to json format
  • jquery.SPServices-0.7.2.js  // For getting the list items

Css file required

  • ui.jqgrid.css  //Style sheet of Grid

HTML controls for jqGrid are as mentioned below.

<div id='tblMain' style="float:left">
  <table  id="list" ></table>
  <div id="pager" style="text-align:center;"></div>

Loading JqGrid on the page load:

  datatype: GetMyData,
  colNames:["Project ID","Project Name","Delivery Manager","ApprovalStatus"],
  colModel:[{name:'ProjectId',index:'ProjectId',align:'left',sortable: true},
            {name:'ProjectName',index:'ProjectName',align:'left',sortable: true },
          {name:'ApprovalStatus',index:'ApprovalStatus',align: 'left',sortable: true }
  pager: true,
  pager: '#pager',
  pageinput: true,
  rowNum: 5,
  rowList: [5, 10, 20, 50, 100],
  sortname: 'ApprovalStatus',
  sortorder: "asc",
  viewrecords: true,
  autowidth: true,
  emptyrecords: "No records to view",
  loadtext: "Loading..."

In the above jqGrid load function I have mentioned the datatype for the grid as GetMyData() which is a function that gets triggerred first.

The GetMyData method has function GetDataOnLoad which uses the SpServices which has the basic operation of getting the list items i.e. GetListItems, which need optional CAML Query property which will fetch the data from list with some WHERE clause.

In the code I have a list called ProjectDetailsList which will contain details of some projects which are inserted by some Project Manager or delivery manager. So the requirement was when a user log in to the system I should get the current login user name and pass the same user name to the “where” clause of query so the grid will contain data of projects to which the current logged in user is assigned as PM or DM.

To get the current login user am using SpServices Operation SpGetCurrentUser.

The method GetTheOrderByType function will make the query part for SpServices.

The functions code is as follows:

function ForGettingUserName() {
  var userName = $().SPServices.SPGetCurrentUser({
      fieldName : "Title",
      debug : false
  return userName;

function GetMyData() {
  sortIdexName = jQuery("#list").getGridParam("sortname"); //Maintaining Consitant SortName after the Sortcol event
  sortOrderName = jQuery("#list").getGridParam("sortorder"); //Maintaining Consistant Sort Order
  Query = GetTheOrderByType(sortIdexName, sortOrderName);
  var CAMLViewFields = "<ViewFields>" +
     + "<FieldRef Name='projectName' /><FieldRef Name='projectID' />"
     + "<FieldRef Name='Title' /><FieldRef Name='deliveryManager' />"
     + "<FieldRef Name='projectSQA' /><FieldRef Name='approvalStatus' />"
     + "<FieldRef Name='projectStartDate' /><FieldRef Name='projectEndDate' />"
     + "<FieldRef Name='sqasiteurl' /><FieldRef Name='ID' />"
     + "</ViewFields>";
  GetDataOnLoad(Query, CAMLViewFields);
Function for Getting the Query Type and the particular WHERE CLAUSE TO send the Query to GetListItem function
function GetTheOrderByType(index, sortOrder, userName) {
  var OrderByType;
  if (index == "ProjectName") {
    if (sortOrder == "desc") {
      OrderByType = "<Query>" +
         + "<Where><Or>" + "<Eq>" + "<FieldRef Name='deliveryManager'/><Value Type='Text'>"
         + userName + "</Value>" + "</Eq>"
         + "<Eq><FieldRef Name='projectManager'/><Value Type='Text'>"
         + userName + "</Value></Eq>" + "</Or></Where>" +
         + "<OrderBy><FieldRef  Name='projectName' Ascending='FALSE' /></OrderBy>" +
         + "</Query>";
    } else {
      OrderByType = "<Query>" +
         + "<Where><Or>" + "<Eq>" + "<FieldRef Name='deliveryManager'/><Value Type='Text'>"
         + userName + "</Value>" + "</Eq>"
         + "<Eq><FieldRef Name='projectManager'/><Value Type='Text'>"
         + userName + "</Value></Eq>" + "</Or></Where>" +
         + "<OrderBy><FieldRef  Name='projectName' Ascending='FALSE' /></OrderBy>" +
         + "</Query>";
  } else if (index == "ApprovalStatus") {
    if (sortOrder == "desc") {
      OrderByType = "<Query>" +
         + "<Where><Or>" + "<Eq>" + "<FieldRef Name='deliveryManager'/><Value Type='Text'>"
         + userName + "</Value>" + "</Eq>"
         + "<Eq><FieldRef Name='projectManager'/><Value Type='Text'>"
         + userName + "</Value></Eq>" + "</Or></Where>" +
         + "<OrderBy><FieldRef  Name='approvalStatus' Ascending='FALSE' /></OrderBy>" +
         + "</Query>";
    } else {
      OrderByType = "<Query>" +
         + "<Where><Or>" + "<Eq>" + "<FieldRef Name='deliveryManager'/><Value Type='Text'>"
         + userName + "</Value>" + "</Eq>"
         + "<Eq><FieldRef Name='projectManager'/><Value Type='Text'>"
         + userName + "</Value></Eq>" + "</Or></Where>" +
         + "<OrderBy><FieldRef  Name='approvalStatus' Ascending='FALSE' /></OrderBy>" +
         + "</Query>";
    return OrderByType;

  //This function gets the data from List using SpServices
  Function GetDataOnLoad(Query, CAMLViewFields) {
      operation : "GetListItems",
      async : false,
      listName : "ProjectDetailsList",
      CAMLQuery : Query,
      CAMLViewFields : CAMLViewFields,
      completefunc : processResult

The processResult is the function which formats the data which can be converted to Json and adds to the JqGrid.
The reason of formatting of data in the following particular format is to make it readable by the Json parser which is json2.js file. I had implemented the same JqGrid in asp.net application with AJAX calls where it was returning the data in this format and some other bloggers also used the same data format in the MVC or asp.net application with the help for JsonHelper class which mainly formats the data returned from the DB

//Processing the XML result to formatted Json so that We can bind data to grid in Json format
function processResult(xData, status) {
  var counter = 0; // Gets the total number of records retrieved from the list (We can also use xData.ItemCount method for counting the number of rows in the data )
  var newJqData = "";

  $(xData.responseXML).SPFilterNode("z:row").each(function () {

    var JqData;
    if (counter == 0) {
      JqData = "{id:'" + $(this).attr("ows_projectID") + "',"
         + "cell:[" + "'" + $(this).attr("ows_projectID") + "','" +
        $(this).attr("ows_projectName") + "','" +
        $(this).attr("ows_deliveryManager") + "','," +
      newJqData = newJqData + JqData;
      counter = counter + 1;
    } else {
      var JqData = "{id:'" + $(this).attr("ows_projectID") + "',"
         + "cell:[" + "'" + $(this).attr("ows_projectID") + "','" +
        $(this).attr("ows_projectName") + "','" +
        $(this).attr("ows_deliveryManager") + "','," +
      newJqData = newJqData + JqData;
      counter = counter + 1;

  FinalDataForGrid(newJqData, counter);

That’s it. Add the data to the grid with the div control and the other page number calculation is for showing the pager.

function FinalDataForGrid(jqData, resultCount) {
  dataFromList = jqData.substring(0, jqData.length - 1);
  var currentValue = jQuery("#list").getGridParam('rowNum');
  var totalPages = Math.ceil(resultCount / currentValue);
  var PageNumber = jQuery("#list").getGridParam("page"); // Current page number selected in the selection box of the JqGrid
  //formatting rows
  newStr = "{total:" + '"' + totalPages + '"' + "," + "page:" + '"' + PageNumber + '"' + ","
     + "records:" + '"'
     + resultCount + '"' + ","
     + "rows:"
     + "[" + dataFromList + "]}";
  var thegrid = jQuery("#list")[0];
  thegrid.addJSONData(JSON.parse(newStr)); //Binding data to the grid which is of JSON Format

And the grid works fine and fast on paging, sorting and also even search, we can make the particular column as hyperlink which I will blog in the next part. Sample grid is as follows and this grid has some extra columns then the mentioned in above code

SPServices Futures: Moving to jQuery’s Deferred Objects and More

As I gear up to work on the next release of SPServices, I want to make some pretty fundamental changes/improvements to the internal plumbing. What I’m hoping to do with this post is to gather any ideas and feedback that the user community has about the implementation before I go too far with things.


Deferred Object and Promises

In all prior versions, we’ve relied on the completefunc (which I made up) to process the results we get back from a Web Services operation call. In the meantime, jQuery has moved forward significantly.

In jQuery 1.5, we got the first deferred object capabilities. I was loathe to change the internal workings of SPServices at the time because we had a substantial user base, even then.

Now the user base is significantly larger (as of this writing, there have been almost 75,000 [!] total downloads), but I think it’s time to add the deferred capabilities into the core SPServices function. More and more people have been exposed to deferred processing capabilities in script at this point, so it will be less of a shock to existing SPServices users (I hope!).

Oddly, I’ve only had one request (at least that I can remember) to add deferred processing into SPServices. It came from a Codeplex user named MgSam in a thread s/he titled JQuery Deferred compatibility, and I’ll admit that I blew it off a bit when he raised it as a possibility.

My strongest impetus for considering this fundamental architecture shift is having seen Scott Hillier talk several times about Utilizing Promises in SharePoint 2013 Apps. In actual fact, his talk applies to JavaScript (and thus jQuery) development more than it applies to SharePoint 2013 development. We can use the deferred methodology even if we’re running our scripts on SharePoint 2007. It’s a way to manage requests in the script and has little to do with the version of SharePoint.

In doing this, my goal will be to keep full backward compatibility so that all existing code continues to function with no changes, but I may deprecate the current completefunc focused methodology, either in this next release or shortly thereafter.

in the simplest form, it will look something like this (borrowing from MgSam’s example in the thread above):

function myCode(someOtherListId) {
    /* Ideally, I should be able to do this call SPListNameFromUrl asynchronously
     * as well, perhaps add an optional "async" parameter
     * which, if used, returns the jqXHR object and invokes
     * a callback upon completion rather than blocking until
     * the data returns. This is not the main point of this code though.
    var currentListId = $().SPServices.SPListNameFromUrl();

    //Call 1
    var a = $().SPServices({
        operation: "GetListItems",
        async: true,
        listName: currentListId,
        completefunc: function(xData) {
            //Do something useful

    //Call 2
    var b = $().SPServices({
        operation: "GetList",
        async: true,
        listName: currentListId,
        completefunc: function(xData) {
            //Do something useful

     //Call 3
     var c = $().SPServices({
        operation: "GetListItems",
        async: true,
        listName: someOtherListId,
        completefunc: function(xData) {
            //Do something useful

     /* Now I want to do something once all 3
      * asynchronous calls have completed. Under the current
      * model, there's no easy way to do this.
      * I either have to hand-roll some ugly code,
      * or contort my completefuncs so that they work with
      * jQuery's Deferred.

Moving toward the deferred object approach will also mean that I’ll replace the simplistic caching mechanism I put into place using the .data() function in v0.7.2. There will be no visible difference in the way it works, but the underlying mechanism will be deferred promises instead.

SharePoint 2007

I’m also considering finally ending development specifically for SharePoint 2007. I’ve continued to use WSS 3.0 as my development environment since the first release of SPServices so that I can “test up” rather than down *and* up.

I still have a lot of people contacting me with questions who are using SharePoint 2007. In fact, some of my best clients are still on SharePoint 2007. It’s still a great platform that works well for many people who are still using it.

That said, there are some capabilities in SharePoint 2010 and now SharePoint 2013 that I haven’t tried to take advantage of because they aren’t there in SharePoint 2007. It isn’t that I want to abandon the SharePoint 2007 folks, but I’m considering making the main focus SharePoint 2010.

I’m very interested in hearing people’s thoughts on that. There’s no easy way for me to know what versions everyone out there has, so chime in.

Version Numbering

These changes will definitely merit a more significant version bump than just going from 0.7.2 to 0.7.3, of course. Is it finally time to ship a 1.0.0? Or maybe I should just decide to go to 5.3.8 or something. The numbers mean nothing, of course, as long as they go in a consistent direction. I’m even toying with 2013.01. Any thoughts on this?

Thanks to everyone out there who uses SPServices, especially those of you who have contacted me to let me know how it has helped you accomplish your business goals. If you have a story to tell, please consider writing it up for my SPServices Stories series. I’m planning to keep that series going as long as there are good stories to tell.

SPServices Stories #9: Developing with Client-side Technologies: jQuery, REST, SPServices and jsRender


Here’s another SPServices Stories post that is a bit older and talks about how you can use SPServices with other popular plugins and frameworks. While this is possible with the CSOM, I’ve always found that SPServices provides a much tighter and more controllable syntax. You probably can’t trust me on that, though, since I wrote it.

Phil Harding’s post from March 2012 called SharePoint: Developing with Client-side Technologies: jQuery, REST, SPServices and JsRender caught my eye because in it he explains how he used the popular JsRender with SPServices. Many people want to use rendering frameworks in general or specifically in conjunction with SPServices, and JsRender has been a popular choice. Knockout,js is probably becoming more popular, but JsRender still has its place. You should evaluate the options based on your own business requirements to decide what’s best for you.

Keep in mind that is doesn’t really matter what version of SharePoint you’re working with, as these techniques can work well with SharePoint 2007, 2010, and 2013.

Phil Harding (@phillipharding) is an independent SharePoint consultant from Manchester, UK, specializing in development for the 2007, 2010 and (he’s hoping really really soon) 2013.

Developing with Client-side Technologies: jQuery, REST, SPServices and JsRender

Having recently been immersed in developing client-side functionality for a SharePoint 2007 project I thought I’d share some of the techniques and tools I’ve used in doing so.

The functional requirements in this project are pretty standard CRUD operations;

  1. Collect data from the user and create new list items
  2. Display and modify existing data
  3. Display formatted lists of existing data

In this post we are going to retrieve a set of list items from a SharePoint list and display that data using a technique very similar to that used by ASP.NET data bound/templatized controls.


The premier tool IMHO for interacting with SharePoint 2007 from the client is the SPServices library by @sympmarc, check out the documentation as the library is pretty extensive.

Here we will be using the SPServices GetListItems method which wraps the SharePoint Lists web service.


If you’re working with SharePoint 2010, you might also consider using the REST interface for retrieving data, although this interface by default returns XML, it can also be configured to return JSON, and the jQuery getJSON() method does exactly that by configuring the $.ajax() call to the REST interface appropriately. Why JSON? Well as with SPServices, using JSON encoded results fits very nicely when using jsRender to render your output markup. Some good examples of how to use the REST interface can be found here.

[important]Ed: Note that as of v0.7.1, SPServices contains a function called $().SPXmlToJson which makes it a no-brainer to convert GetListItems results to JSON.[/important]

Choosing a Client Side Data Retrieval Interface

If you’re using SharePoint 2007, your options are (very) limited. By limited I mean, that pretty much your only sane option is SPServices, there are other options of course, 2007 has the [SOAP] List webservice which you could grok against – you’ll have to write a lot of javascript if you want to go that route, but seriously, why bother, SPServices has you more than covered.

If you’re using SharePoint 2010 or 2013, you’ve got the JSOM interface which you can use to read list data, frankly though, given the amount of code you have to write just to read some data, I almost never use the JSOM for this.

SPServices of course is a valid choice to make – as with all things choose the right tool to accomplish your goal. Personally, I tend to use REST when my data needs are typically one-way, i.e. reading, even then I may defer to SPServices if my query is sufficiently complex – the REST interface is pretty damn flexible but it won’t allow you to model complex queries in the way that CAML does.

For updating data back to lists, again you can use REST, JSOM or SPServices. For me, I might typically use JSOM to update list items, sure I can do this using REST, but to do this you’ll have to manage the ETag – another piece of state to maintain (see the Concurrency Management section for more information). I also came across a javascript library recently called data.js, which seems to be a wrapper over OData services, I haven’t used it yet but it definitely looks interesting.

Finally, SPServices has the UpdateListItems method which serves both creating and updating data.


Having retrieved your data from SharePoint, you could of course, write quite a bunch of grungy JavaScript and DOM code to create the rendered display, however there is a much better way, and that is to use something latterly called jQuery Templates, which, like ASP.NET data-bound or templatized controls, allows you to write the markup template for your output and include bits which bind to the data being displayed.

jQuery Templates are officially deprecated in favour of a new but similar technology called jsRender by Boris Moore.

jsRender seems to be gaining a lot of traction and there is a growing amount of information available out there, and the documentation provided by Boris is more than enough to get you going.

Retrieving List Items.

So lets get going, first we’ll write the SPServices code to retrieve a set of list items, which we’ll then iterate over and place into an array of JSON objects (required by jsRender).

function GetListItemData() {
  var container=$('#container');

  // get list data
  var datarows = [];
    operation: "GetListItems",
    async: false,
    listName: "The List Title",
    completefunc: function(xData, Status) {
      $(xData.responseXML).SPFilterNode("z:row").each(function() {
        var x={
          title: $(this).attr('ows_Title'),
          id: $(this).attr('ows_ID'),
          reviewoutcome: $(this).attr('ows_ReviewOutcome')
  ..... // display list data

Here we use SPServices GetListItems to retrieve the data, amongst other things we supply the following parameters;

  1. operation: “GetListItems”
  2. Optional webURL parameter, if not supplied SPServices will use the equivalent of SPContext.Current [the current Web]
  3. listName: “The List Title”
  4. CAMLViewFields: the list of fields we want returned by the query
  5. CAMLQuery: the CAML query in the form; “<Query><Where>….</Where><OrderBy>….</OrderBy></Query>”

I also specify a completion function which parses and iterates over the returned rows, creates a JSON object and adds it to an array.

Note: SPServices also provides the SPXmlToJson function which will convert the returned rows to an array of JSON objects.

Next we’ll use jsRender to display the data we just retrieved using the supplied template (#reviewTemplate), in this case the template with id reviewTemplate.

function GetListItemData() {
  var container=$('#container');

  var datarows = [];

  ..... // get list data

  // display list data
  if (datarows.length > 0) {
    $(container).html( $("#reviewTemplate").render(datarows) );
  } else {
    $(container).html("There are no items to display.");

The return value from jsRender is then set as the html value of a container DIV or other element.

jsRender Templates.

So what’s a jsRender template? Quite simply it is a <SCRIPT /> block with an ID value and a type set to text/x-jsrender, as shown below.

<script id="reviewTemplate" type="text/x-jsrender">
<TABLE cellSpacing="0" cellPadding="0" width="100%">
      <TD style="width:400px">
        <A title="{{:title}}" href="{{:~reviewurl(id)}}">{{:title}}</A>
      <TD style="width:20px">
        {{outcome reviewoutcome /}}
        {{if reviewoutcome=='Undecided' tmpl='#reviewundecided' /}}
        {{if reviewoutcome=='Accepted' tmpl='#reviewaccepted' /}}
        {{if reviewoutcome=='Rejected' tmpl='#reviewrejected' /}}
<script id="reviewundecided" type="text/x-jsrender">
  <IMG style="" title="this is undecided." border="0" src="http://sp2007/_layouts/images/erg-0.gif" />
<script id="reviewaccepted" type="text/x-jsrender">
  <IMG style="" title="this was accepted." border="0" src="http://sp2007/_layouts/images/erg-2.gif" />
<script id="reviewrejected" type="text/x-jsrender">
  <IMG style="" title="this was rejected." border="0" src="http://sp2007/_layouts/images/erg-1.gif" />

Here we’re showing 4 jsRender templates, reason being that a feature of jsRender allows you to use template composition, i.e. to use different templates to render different parts of the output. In this case we have 3 different templates to render an <IMG…/> element according to the value of the “reviewoutcome” object property (which is the listitems ows_ReviewOutcome column value). To summarise whats going on I’ll briefly describe each of the jsRender features we’re using;

Expression Evaluation / Output an Object Property Value

Use the {{:  expression  }} tag to evaluate an expression or output the property value of the current item, this form does not perform HTML encoding of the output

Use the {{> expression  }} tag to evaluate an expression or output the property value of the current item, this form performs HTML encoding of the output

Call a Custom Helper Function

Use the {{:~ customhelper(expression)  }} tag to call a custom helper function that you have written, which optionally accepts parameters, and returns the desired output – in the sample above we created a helper function called reviewurl to build a composite HREF value for an <A /> element.

See below for help writing a custom helper function.

Conditional Processing : If/Else

Use {{if  expression }}…{{/if}} {{else  expression }}…{{/else}} tags to perform conditional processing or branching of the template – in the sample above we used conditional processing to choose a different jsRender template to build <IMG /> elements based on the value of the current items reviewoutcome property value.

Call a Custom jsRender Tag

In the above example we used conditional processing to chose a jsRender template to display different <IMG /> elements, another method of doing this is to write a custom jsRender tag which will do the same thing. To use a custom jsrender tag you have written, use the following form {{mytagname expression /}}. In the sample above we created a custom tag called outcome which accepted the reviewoutcome property value of the current item and returned an <IMG /> element.

See below for help writing a custom tag function.

To create a custom helper function;

A custom helper function, as you might imagine, is simply a JavaScript function which optionally accepts parameters and returns something for display. Usefully the function can reference global variables in the page, as you can see I’m referencing global variables g_rlid and g_thispageurl.

/* jsRender helper function */
$.views.helpers( {
  reviewurl:function(fid) {
    var m="mypage.aspx?";
    return m;

To create a custom jsRender Tag function;

A custom tag function, is also a JavaScript function which optionally accepts parameters and returns something for display.

/* jsRender custom tag */
outcome: function(value) {
var ret='';
switch(value) {
case 'Undecided':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-0.gif' />";
case 'Accepted':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-2.gif' />";
case 'Rejected':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-1.gif' />";
return ret;

Putting all this together we get something that is ripe for a little CSS beautification.


There’s much more to go at in jsRender, including some interesting effects involving hoverstate and re-rendering of item display using dynamic templates. Hopefully the documentation (and feature-set) will continue to improve and that this post has provided a leg up should you wish to start using it.

SPServices Stories #8 – CEWP, Nintex, jQuery, SPServices and the Client API


I’ve been keeping a list of older posts about using SPServices that I’ve seen on the InterWebz for that day when I finally got around to doing something like SPServices Stories. This one is from Dan Stoll (@_danstoll) at Nintex, and was originally published on Dan’s blog back in February, 2012 as CEWP, Nintex, jQuery, SPServices and the Client API.

To me, the fact that someone at Nintex would choose to use SPServices to accomplish something with workflows is pretty telling, in a positive way. The folks over at Nintex know what they are doing, and if they choose to use SPServices as spackle to fill in some of the gaps in SharePoint, then it’s testament to the value of it.

In SPServices Stories #7, John Liu showed an example of starting a workflow with SPServices, and here Dan gives us some of the details under the covers.

CEWP, Nintex, jQuery, SPServices and the Client API

I had a requirement that required a not so difficult solution but tricky.. The first requirement was that a webpart had to be embedded in to a publishing page layout that was used over multiple sites in the farm. The next requirement was to only show this webpart to a select group of people.. Easy right ? Using audiences should work a treat…. Wrong. . Embedding the CEWP you can still put in the audience but it doesn’t work because it isn’t in a webpart zone… Ok, so let’s put a webpart zone in.. Pop in my custom CEWP webpart.. The audience set.. but still no joy.. Audience is working but because my webpart is in a webpart zone, it doesn’t show up.

So now I’m faced with .. “How do I get a default webpart to appear on 500 + pages that already exist?? ” Back to square one.. There has to be a way of telling a Div to hide if you aren’t part of a group of some kind. So let’s look at this.. Here is an extract of my page layout

<div class="certifiedPanel" style="display: none;">
  <div class="article-before-wp">
    <h3 class="ms-standardheader ms-WPTitle">
  <div class="article-meta article-wp">
  <table style="width: 100%;">
        <th class="style1" style="width: 36%;">Contact:</th>
        <td id="pubContact"></td>
        <th class="style1" style="width: 36%;">Last Modified:</th>
        <td id="modifiedOn"></td>
      <tr style="display: none;">
        <th style="width: 36%;">Last Review Date:</th>
        <td id="lastReviewedOn"></td>
        <th class="style1" style="width: 36%;">Next Review
        <td id="reviewedOn"></td>
        <th class="style1" style="width: 36%;">Certified Date:</th>
        <td id="certifiedOn"></td>
        <th class="style1" style="width: 36%;">HYway DOCID:</th>
        <td id="docId"></td>
        <th class="style1" style="width: 36%;">Reference ID:</th>
        <td id="refId"></td>
  </table>Managers PanelNoneUse for formatted text, tables, and
  <dir>Default</dir>Cannot import this Web
  <div class="article-after-wp"></div>

You’ll see in the first line, I have included a “display:none” style on the DIV called “certifiedPanel” The rest of it, well the first half of the Certified Panel shows a few fields from the content type, eg Modified Date, ID etc etc.. The second half is the CEWP that has a text file as it’s source of content ‘startworkflow.txt’ .. This is where part 2 of the story begins..

The other requirements is that this “Panel” had to show indicators as to the phases of the document, it also had to have a link to the version history of the document and there also had to be a couple of workflows that could be executed against the page (content type) from this panel.. Firstly, let me show you the panel

The 3 indicators shown here, show the manager at a quick glance that the document isn’t certified (Yellow) because the review date has passed, it isn’t ready for Review either (Red) as the Contact hasn’t been filled in. The only parameter that is ok is that the Review date as been set with “something” (Green)


As you can see the document has been modified since being certified so this document is now no longer certified as changes may have been made to the content…

Ok so how did we do this.. I’ll post the full contents of the startworkflow.txt file and we can work from there. (the Startworkflow.txt) is used with the Content Editor Webpart to display on the page

[Ed: I’ve split the HTML and JavaScript sections for better readability.]

var $ = jQuery;
function SetPageIndicators() {

  //get the certified and reviewed indicators
  var certifiedOn = new Date($(&quot;#certifiedOn&quot;).text());
  var hasCertDate = !isNaN(certifiedOn);
  var modifiedOn = new Date($(&quot;#modifiedOn&quot;).text());
  var hasModDate = !isNaN(modifiedOn);
  var reviewOn = new Date($(&quot;#reviewedOn&quot;).text());
  var hasReview = !isNaN(reviewOn);
  var lastReviewedOn = new Date($(&quot;#lastReviewedOn&quot;).text());
  var haslastReviewedOn = !isNaN(lastReviewedOn);
  var baseUrl = &quot;/Style%20Library/hyway/Images/&quot;;

  //set certified icon
  var certImg = $(&quot;#certImg&quot;);
  if (!hasCertDate)
    certImg.attr(&quot;src&quot;, baseUrl + &quot;statusRed.png&quot;);
  if (hasCertDate)
    certImg.attr(&quot;src&quot;, baseUrl + &quot;statusGreen.png&quot;);
  if ((hasModDate &amp;&amp; hasCertDate) &amp;&amp; (modifiedOn.setDate(+1) &gt; certifiedOn))
    certImg.attr(&quot;src&quot;, baseUrl + &quot;statusYellow.png&quot;);

  //set lastReviewedOn icon
  var lastReviewedOnImg = $(&quot;#lastReviewedOnImg&quot;);
  if (!haslastReviewedOn)
    lastReviewedOnImg.attr(&quot;src&quot;, baseUrl + &quot;statusRed.png&quot;);
  if (haslastReviewedOn)
    lastReviewedOnImg.attr(&quot;src&quot;, baseUrl + &quot;statusGreen.png&quot;);
  if (lastReviewedOn &gt; certifiedOn)
    lastReviewedOnImg.attr(&quot;src&quot;, baseUrl + &quot;statusYellow.png&quot;);

  //set review icon
  var reviewImg = $(&quot;#reviewImg&quot;);
  if (!hasReview) {
    reviewImg.attr(&quot;src&quot;, baseUrl + &quot;statusRed.png&quot;);
  } else {
    if (reviewOn &gt; new Date()) {
      reviewImg.attr(&quot;src&quot;, baseUrl + &quot;statusGreen.png&quot;);
    } else {
      reviewImg.attr(&quot;src&quot;, baseUrl + &quot;statusYellow.png&quot;);

  //Hide the Panel from those who don't need it
  ExecuteOrDelayUntilScriptLoaded(function () {
    var ctx = SP.ClientContext.get_current();
    var web = ctx.get_web();

    // change ID based on
    // http://devserver/OurProcesses/_layouts/people.aspx?MembershipGroupId=551
    // http://server/OurProcesses/_layouts/people.aspx?MembershipGroupId=752
    var group = web.get_siteGroups().getById(551);
    var users = group.get_users();
    var user = web.get_currentUser();
    ctx.executeQueryAsync(Function.createDelegate(this, function () {

        // success
        for (var i = 0; i &lt; users.get_count(); i++) {
          var u = users.get_item(i);
          if (u.get_loginName() == user.get_loginName()) {
            //alert(&quot;found you&quot;);

      }), Function.createDelegate(this, function () {}));
  }, &quot;sp.js&quot;);
function StartWorkflow(ItemURL, WorkflowName) {
  var waitDialog = SP.UI.ModalDialog.showWaitScreenWithNoClose('working on it….', 'Please wait while Gnomes get this sorted for you...', 76, 330);

  // start the workflow manually, then take the TemplateID from the URL of the Workflow starting form
  // this is no good since it changes when you republish workflow...
  // get TemplateID first for item.
    operation : &quot;GetTemplatesForItem&quot;,
    item : ItemURL,
    async : false,
    completefunc : function (data, status) {
      var workflowTemplateID;
      if (status == &quot;success&quot;) {
        $(data.responseXML).find(&quot;WorkflowTemplates &gt; WorkflowTemplate&quot;).each(function (i, e) {

          // hard coded workflow name
          if ($(this).attr(&quot;Name&quot;) == WorkflowName) {
            var guid = $(this).find(&quot;WorkflowTemplateIdSet&quot;).attr(&quot;TemplateId&quot;);
            if (guid != null) {
              workflowTemplateID = &quot;{&quot; + guid + &quot;}&quot;;
      } else {

        // error can't find template
        alert(status + &quot; : &quot; + data.responseText);

      // start workflow now with obtained templateID. Note this must run within the completeFunc of the first webservice call
        operation : &quot;StartWorkflow&quot;,
        item : ItemURL,
        templateId : workflowTemplateID,
        workflowParameters : &quot;&lt;root /&gt;&quot;,
        completefunc : function (data, status) {
          if (status == &quot;error&quot;) {
            alert(status + &quot; : &quot; + data.responseText);
          } else {
function certImg_onclick() {}

function ShowVersionHistory() {
  if (!_spPageContextInfo) {
  var options = {
    tite : &quot;Versions&quot;,
    url : _spPageContextInfo.webServerRelativeUrl + &quot;/_layouts/Versions.aspx?list=&quot; + _spPageContextInfo.pageListId + &quot;&amp;ID=&quot; + _spPageContextInfo.pageItemId,
    allowMaximize : false,
    showClose : true,
    width : 800,
    height : 500,


<table style="width: 100%">
    <img id="certImg" alt="Page Certified Indicator" src=""
    onclick="return certImg_onclick()" height="20px"
    width="20px" />Certified</td>
      <a href="javascript:StartWorkflow(document.location, 'Super Stamp')">
      Certify Process Page</a>
      <img id="reviewImg" alt="Page Review Indicator" src=""
      height="20px" width="20px" />Reviewed</div>
      <a href="#" onclick="javascript:ShowVersionHistory();">View
      Modification History</a>
    <img id="lastReviewedOnImg"
    alt="Page Ready for Review Indicator" src=""
    onclick="return lastReviewedOnImg_onclick()" height="20px"
    width="20px" />Ready for Review</td>
      <a href="javascript:StartWorkflow(document.location, 'ReadyForReview')">
      Ready for Review</a>

As you can probably guess there is are a couple of parts to this txt file..

  1. The first being jquery switching the indicators depending on the rules set and the values of certain date fields.
  2. The second part is using the client API where we are setting the ID of the SP group (note you can’t have any nested SP or AD groups here) that has access to this panel.. This then sets the DIV certifiedPanel display to show.
  3. Part 3 is using SPServices http://spservices.codeplex.com/ to call Nintex Reusable Workflows that are bound to the custom content type that I am using for these Publishing Pages. As Nintex Workflow use the same infrastructure as SharePoint workflows, the rich feature set of SPServices, allow me to start my Nintex Workflows and a whole host of other things.

These workflows not only update the Page in question but also assign tasks to the review committee, and maintain a centralised list of all pages that are currently under review, / are due for review or don’t comply to the companies guidelines..

On approval of the task, the pages are certified within the companies “Processes” site. The JavaScript indicators reflect its new status, and the document is removed form the centralised management list.