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.

SPServices

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

Introduction

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.

SPServices.

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.

REST.

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.

jsRender.

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');
  $(container).html("");

  // get list data
  var datarows = [];
  $().SPServices({
    operation: "GetListItems",
    webURL:"http://sp2007/sites/demo",
    async: false,
    listName: "The List Title",
    CAMLViewFields:"",
    CAMLQuery:"........",
    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')
        };
        datarows.push(x);
      });
    }
  });
  ..... // 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');
  $(container).html("");

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

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?";
    m+="form=Display";
    m+="&list="+g_rlid;
    m+="&id="+fid;
    m+="&Source="+g_thispageurl;
    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 */
$.views.tags({
outcome: function(value) {
var ret='';
switch(value) {
case 'Undecided':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-0.gif' />";
break;
case 'Accepted':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-2.gif' />";
break;
case 'Rejected':
ret="<IMG style='' title='' border='0' src='http://sp2007/_layouts/images/erg-1.gif' />";
break;
}
return ret;
}
});

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

http://platinumdogs.files.wordpress.com/2012/03/sps-01.png

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

Introduction

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">
      <span>Details</span>
    </h3>
  </div>
  <div class="article-meta article-wp">
  <table style="width: 100%;">
    <tbody>
      <tr>
        <th class="style1" style="width: 36%;">Contact:</th>
        <td id="pubContact"></td>
      </tr>
      <tr>
        <th class="style1" style="width: 36%;">Last Modified:</th>
        <td id="modifiedOn"></td>
      </tr>
      <tr style="display: none;">
        <th style="width: 36%;">Last Review Date:</th>
        <td id="lastReviewedOn"></td>
      </tr>
      <tr>
        <th class="style1" style="width: 36%;">Next Review
        Date:</th>
        <td id="reviewedOn"></td>
      </tr>
      <tr>
        <th class="style1" style="width: 36%;">Certified Date:</th>
        <td id="certifiedOn"></td>
      </tr>
      <tr>
        <th class="style1" style="width: 36%;">HYway DOCID:</th>
        <td id="docId"></td>
      </tr>
      <tr>
        <th class="style1" style="width: 36%;">Reference ID:</th>
        <td id="refId"></td>
      </tr>
    </tbody>
  </table>Managers PanelNoneUse for formatted text, tables, and
  images.true2NormaltruetruetruetruetruetruetrueModeless
  <dir>Default</dir>Cannot import this Web
  Part.true00000000-0000-0000-0000-000000000000g_73d6de6b_445e_4d9a_8443_c05b20548336/OurProcesses/Documents/startworkflow.txt</div>
  <div class="article-after-wp"></div>
</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)

hy_details-300x300

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;
$(document).ready(SetPageIndicators);
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);
    ctx.load(group);
    var users = group.get_users();
    ctx.load(users);
    var user = web.get_currentUser();
    ctx.load(user);
    ctx.executeQueryAsync(Function.createDelegate(this, function () {

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

            $(&quot;.certifiedPanel&quot;).show();
          }
        }
      }), 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.
  $().SPServices({
    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);
        return;
      }

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

function ShowVersionHistory() {
  if (!_spPageContextInfo) {
    return;
  }
  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,
  };
  SP.UI.ModalDialog.showModalDialog(options);
}

 

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

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.

SPServices Stories #7 – Example Uses of SPServices, JavaScript and SharePoint

Introduction

I’ve been aware that John Liu (@johnnliu) is a fan of SPServices for some time now. He occasionally tweets about things he’s up to, and they always sound intriguing.

Recently, I asked him if he’d like to share any of his own SPServices Stories, and he did a post for me on his blog. In the post, John shows three great examples of how you can slide SPServices under some truly impressive functionality that greatly improves the overall SharePoint user experience.

There’s another SPServices Story coming up from Dan Stoll (@_danstoll) which goes into some of the details on John’s first example below.

Without further ado, here’s John’s first SPServices Story. I expect that John will have a few more SPServices Stories to share with us over time.

Example uses of SPServices, JavaScript and SharePoint

I wanted to write about spservices.codeplex.com from Marc D Anderson – we’ve found ourselves using this really special library time and again across different projects to talk back to SharePoint quickly.

Starting Workflows

Here’s a page from one of our Process Wiki articles.

Process Wiki Example

  • We have a special “Contributor-only” Web Part on the right.
  • It shows the various workflow status’ on the current page, as traffic light bubbles.
  • The “Certify Process Page” calls a JavaScript function that calls StartWorkflow via SPServices.
  • The workflow is a Nintex workflow and triggers a significant multistage approval process.  But you can use StartWorkflow to start SharePoint workflows as well.

Getting List Data, Lots of List Data

Here’s our task list, represented as a task board.

Task Dashboard

  • This one is completely done with SPServices to get the list items
  • Convert the objects to JSON using SPServices.SPXmlToJson
  • Then binding the objects to UI via Knockout
  • There’s jQuery UI’s drag and drop in play, so we can change the Task’s status by dragging the task across a different column.
  • Update task using SPServices’ UpdateListItems call.
  • And some nice CSS.
  • This particular page also runs via SharePoint 2010′s OData listdata.svc, but is completely viable with SPServices on SP2007 as well.

Getting User Profiles via Search

Here’s our People page.

People Page

  • First, get SharePoint to index your people.
  • Use SPServices to call SharePoint search to return a bunch of people, including their picture (one would say, especially their picture).
  • Here I use Knockout to render the pictures.  When clicked, each one opens that user’s My Site page.
  • There’s a filter box on the top right, as well as “fake” refinements on the left hand side that allows us to re-query SharePoint search for filtered people.
  • One possible idea here would be to use SPServices’ User Profile Service support and talk directly to the User Profile service if you want to skip the search service.

Summary

A quick post of 3 recent JavaScript customizations that heavily used SPServices.  Hope that give you guys a lot of ideas.  Let me know what you guys think.