A Compendium of MOSS List Access and Manipulation Techniques using Services and RPC – Part 3

 

See part 1 here – http://curriersblog.spaces.live.com/blog/cns!1FE2EFF5F31CEDCA!332.entry

See part 2 here – http://curriersblog.spaces.live.com/blog/cns!1FE2EFF5F31CEDCA!339.entry

Introduction

This next installment on accessing and manipulating list data will focus on client side techniques using JavaScript and asynchronous methods with SOAP. I’ll start with a quick intro to JQuery (extremely quick) as a vehicle to accomplish my goals here. Other companion libraries will be called out with their respective links as needed. The end result will be a web page containing only JavaScript and HTML acting as a simple frontend browser client into SharePoint. That page can stand alone or be integrated as a page directly within a SharePoint site.

JQuery Preliminary

JQuery is a style of JavaScript programming and associated API that allows for highly succinct chaining of method results. Result chaining maintains DOM context within a single statement so several operations can take place on selected DOM elements. DOM elements are selected using a query syntax. See http://curriersblog.spaces.live.com/blog/cns!1FE2EFF5F31CEDCA!253.entry for a newbie example with SharePoint calendars. Of course, see http://jquery.com for the best examples and tutorials. Microsoft has adopted JQuery and it’s now a big part of the Microsoft development story moving forward.

A ubiquitous JQuery construct is testing that a page has loaded. This looks as follows

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/jquery-1.3.2.min.js"></script>

<script>

        $(document).ready(function() {

                // Our JS/JQuery code goes here because we know the page is now loaded at this point

        });

</script>

Ok, that’s all on JQuery for now.

SOAP Preliminary and Other Editorial Comments

The next bits of information will focus on SOAP (Simple Object Access Protocol – http://www.w3.org/TR/soap/). The SOAP protocol is another big topic that many folks feel is not the best protocol for communicating with SharePoint servers. Alternative protocols, such as REST based protocols, are coming on fast in the development world (http://msdn.microsoft.com/en-us/netframework/cc950529.aspx) but fall into a different set of use cases. The SOAP protocol is not going away very soon (if at all) and is continually extended with new and allied web standards such as ws-security. Alternatively, REST (Representational State Transfer – http://www.ics.uci.edu/~fielding/talks/webarch_9805/) is simple and potentially more performent. Third party vendors are providing SharePoint REST based interfaces as well. I intend to make REST the subject of a future entry.

What’s the bottom line – REST is GREAT for building JavaScript based clients because of its simplicity. SOAP is better for use cases that demand standards already adopted by SOAP or if no other options are possible. Currently SharePoint 2007 only supports SOAP based services (using .asmx end points). 

So now let’s start with what’s required for a SOAP request. The following is the xml payload the browser must send. Also see http://www.w3schools.com/soap/soap_example.asp    

<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Body xmlns:m="http://www.example.org/stock">
  <m:SomeMethod>
    <m:SomeParameter>My Param Value</m:SomeParameter>
  </m:SomeMethod>
</soap:Body>

</soap:Envelope>

The above is what’s needed to make a request to the server specifying the method we need. The next requirement is to send this envelope asynchronously to the server. JQuery has a built in ajax method that wraps the native XMLHTTPRequest object see – http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx

As a slight digression lets look at a simple JQuery RSS reader (this is a fairly common asynchronous example) using the ajax wrapper

<html>

<head><title>SPClient</title>

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/jquery-1.3.2.min.js"></script>
<script type="text/javascript">

$(document).ready(function() {
 

   var feed = ‘http://msdn.microsoft.com/en-us/magazine/rss/default.aspx?z=z&all=1′;

   $.ajax({
             url: feed,
             type: ‘POST’,
             dataType: ‘xml’,
             timeout: 5000,
             error: function(){
                  alert(‘Error loading XML document’);
             },
             success: function(xml){

             $(xml).find(‘item’).each(function() {    <== note the chaining

                        var $item = $(this);
                        var title = $item.find(‘title’).text();
                        var link = $item.find(‘link’).text();
                        var description = $item.find(‘description’).text();
                        var pubDate = $item.find(‘pubDate’).text();

                        var html = "<div class="entry"><h4 class="postTitle">" + title + "</h4>";
                        html += "<em class="date">" + pubDate + "</em>";
                        html += "<p class="description">" + description + "</p>";
                        html += "<a href="" + link + "" target="_blank">Read More >></a></div>";

                        $(‘#feedContent’).append($(html));

                  });

             }

         });

});

</script>

</head>

<body>

<div id="feedContent"></div>

</body>

</html>

Without further explanation the results of the above script is now shown in the next image. 

clip_image001

Great, but with RSS you simply need the URL. For SOAP, XML needs to be constructed and added to the HTTP request that targets the method requested and its associated parameters. If you take the example for GetList (see part 2 of this this series – http://curriersblog.spaces.live.com/blog/cns!1FE2EFF5F31CEDCA!339.entry) you will see the required XML “envelope” that’s sent back and shown here via fiddler (http://www.fiddler2.com/fiddler2/). Note the status of 200, this must be checked as part of the server response in the client code. For a little fun, take the examples from part 2 of this series (install fiddler first) and examine the SOAP envelop  constructed by the various calls out to SharePoint (go ahead its fun!) 

image

Here is the XML if the above image is difficult to read

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
           <GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/">
                 <listName>Rooms</listName>
           </GetList>
    </soap:Body>
</soap:Envelope>

Ok, lets adapt this RSS ajax sample code above to use the SOAP protocol. Let’s replace and eliminate the RSS feed related statements with ones that handle the SOAP request. Here is the new code (just the JQuery script itself). Remember, this is not “production” code so the use of error trapping should be added for such a purpose. 

<script type="text/javascript">

$(document).ready(function() {
   var serviceURL = ‘http://team.currier.com/_vti_bin/lists.asmx’;

   var envelope = ‘<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                   <soap:Body>
                      <GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/">
                         <listName>Rooms</listName>
                      </GetList>
                   </soap:Body>
                   </soap:Envelope>’;

   $.ajax({
             url: serviceURL,
             type: ‘POST’,
             dataType: ‘xml’,
             data: envelope,
             contentType: ‘text/xml; charset="utf-8"’,
             success: function(xml){

                     $(xml).find(‘List’).each(function() {

                      // *** remember this .NET code from part 2? ***
                      // lets look at some List attibutes
                      // string listguid = node.Attributes["ID"].Value;
                      // string listtitle = node.Attributes["Title"].Value;
                      // string listName = node.Attributes["Name"].Value;
                      // string listversion = node.Attributes["Version"].Value;
                      // string listdescription = node.Attributes["Description"].Value;

                      // *** here is the same code recast as Jquery/javascript
                      var listguid = $(this).attr(‘ID’);
                      var listtitle = $(this).attr(‘Title’);
                      var listName = $(this).attr(‘Name’);
                      var listversion = $(this).attr(‘Version’);
                      var listdescription = $(this).attr(‘Description’);

                      // lets place some of these values on the web page
                      var html = "<table width=’400px’ border=’1′>";
                      html += "<tr><td>List GUID</td><td>"+ listguid +"</td></tr>";
                      html += "<tr><td>Title</td><td>"+ listtitle +"</td></tr>";
                      html += "<tr><td>Name</td><td>"+ listName +"</td></tr>";
                      html += "<tr><td>Version</td><td>"+ listversion +"</td></tr>";
                      html += "<tr><td>Description</td><td>"+ listdescription +"</td></tr>";
                      html += "</table>";

                      $(‘#resultarea’).append($(html));
                   });

               }

          });

   });

</script>

Here is the result – wonderful! This means we are talking to SharePoint via a SOAP service using JavaScript alone. 

image

If you want to see this using the “GetListCollection” method then here’s an excellent example from Jan Tielens blog (which I used as source material preparing for this post) http://weblogs.asp.net/jan/archive/2009/04/09/calling-the-sharepoint-web-services-with-jquery.aspx

Great, so now its time to move on – You may agree that calling web services from JavaScript is tedious at best. Developing a page with any complexity really speaks to using some kind of JavaScript SOAP wrapper. No worries for two good reasons – (1) If you followed along so far you probably understand more than most about the machinery of calling SOAP services and (2) a true JavaScript SOAP wrapper does exist for SharePoint.

Using a JavaScript SOAP API

Here are links to some libraries and samples

http://plugins.jquery.com/node/2604 

http://darrenjohnstone.net/2008/07/22/examples-for-the-sharepoint-and-office-live-javascript-api/

http://blog.nvise.com/2008/10/calling-sharepoint-web-services-using-jquery/

By far and away my favorite library is Darren Johnstone’s work noted in the second link. I won’t bore you with samples that are provided on his site except for a couple I’ll use for completing my SharePoint client. However, in all cases JQuery will be driving the code even though Darren’s library was not originally rolled into JQuery.

Preparing the client – A UI Detour

Lets take a little break and discuss the client page and its associated UI. One very “web 2.0” thing we can do is create a popup window of contextual information (some list of data, perhaps user profiles, forms, and other images) without requiring a page transition. One thing you often see within SharePoint is that submit actions take you to another page then, after the action is complete, returns you to the starting page. This is not a great user experience and don’t forget that ugly postback.

My use case is simple. Create a popup, a “model dialog” containing a grid of list rows that activates from the current SharePoint page – to achieve this I located a very simple JQuery grid library that I thought was easy enough to use and still meet my simple requirements.  Here is that link http://digitalbush.com/projects/jquery-grid-plugin/

Keep in mind highly developed JQuery grid libraries do exist around the web that potentially meet more complex requirements, one example is here http://www.trirand.com/blog/

This is an example of the look of the grid page provided by the library I used from digitalbush.

clip_image001[1]

The code that generates this looks is as follows, and is pretty much the demo code provided.

<html>

<head>

<title>SPClient</title>

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/jquery-1.3.2.min.js"></script>
<script src="http://team.currier.com/_layouts/jquery/jquery.grid.core.js" type="text/javascript"></script>

<style>
    .selected{background-color:limegreen !important;}
    .activated{background-color:tomato !important;}
    .even {background-color:#efefef}
    .odd {background-color:#cde}
    #test thead tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test tfoot tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test{
        border-left: 1px solid #CBC7B8;
        border-right: 1px solid #CBC7B8;
    }
</style>

<script type="text/javascript">

$(document).ready(function() {
var t=$("#test");
        t.grid({           
            navigate:{
                maintainSelection:false       
            },
            scroll:{
                width:"600",
                height:"300",
                colWidths:[200,200,200]
            },
            stripe:true,
            columnResize:true
        });       
        var rows=[];
        for(var i=0;i<500;i++)
            rows.push(["Test","Row",i]);
        t.addRow(rows);
});

</script>

</head>

<body bgcolor="#DDDDDD">

<table id="test" border=0 cellspacing=0 cellpadding=0 tabindex=1>
    <thead>
        <tr><th>Header1</th><th>Header2</th><th>Header3</th></tr>
    </thead>
    <tfoot>
        <tr><th>Foot1</th><th>Foot2</th><th>Foot3</th></tr>
    </tfoot>
    <tbody>
    </tbody>
</table>

</body>

</html>

The next step in our development is to place the grid into a modal popup of some kind. Ok, lets cheat a little bit and grab a decent JQuery sample for a model box. Because this post is not about understanding the intricacies of JQuery and to save a little time for a JQuery newbie such as myself, lets use some code from this link – http://www.queness.com/post/77/simple-jquery-modal-window-tutorial

We now combine the grid code with the modal code with the result that looks as follows. Terrific!

clip_image001[3]

Then click and see this

image

Here is the code

<html>

<head>

<title>SPClient</title>

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/jquery-1.3.2.min.js"></script>
<script src="http://team.currier.com/_layouts/jquery/jquery.grid.core.js" type="text/javascript"></script>

<style>
    .selected{background-color:limegreen !important;}
    .activated{background-color:tomato !important;}
    .even {background-color:#efefef}
    .odd {background-color:#cde}
    #test thead tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test tfoot tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test{
        border-left: 1px solid #CBC7B8;
        border-right: 1px solid #CBC7B8;
    }

        /* Z-index of #mask must lower than #boxes .window */
        #mask {
                 position:absolute;
                 z-index:9000;
                 background-color:#FFF;
                 display:none;
               }

        #boxes .window {
                 position:absolute;
                 width:440px;
                 height:200px;
                 display:none;
                 z-index:9999;
                 padding:20px;
               }

        /* Customize your modal window here, you can add background image too */
        #boxes #dialog {
               width:375px;
               height:203px;
               border-style:solid;
               border-width:5px;
               background-color: #DDDDDD
              }

</style>

<script type="text/javascript">

$(document).ready(function() {
var t=$("#test");
        t.grid({           
            navigate:{
                maintainSelection:false       
            },
            scroll:{
                width:"600",
                height:"300",
                colWidths:[200,200,200]
            },
            stripe:true,
            columnResize:true
        });       
        var rows=[];
        for(var i=0;i<500;i++)
            rows.push(["Test","Row",i]);
        t.addRow(rows);

    //select all the a tag with name equal to modal

    $(‘a[name=modal]’).click(function(e) {
        //Cancel the link behavior
        e.preventDefault();
        //Get the A tag
        var id = $(this).attr(‘href’);
        //Get the screen height and width
        var maskHeight = $(document).height();
        var maskWidth = $(window).width();
        //Set height and width to mask to fill up the whole screen
        $(‘#mask’).css({’width’:maskWidth,’height’:maskHeight});
        //transition effect       
        $(‘#mask’).fadeIn(1000);   
        $(‘#mask’).fadeTo("slow",0.8);   
        //Get the window height and width
        var winH = $(window).height();
        var winW = $(window).width();
        //Set the popup window to center
        $(id).css(‘top’,  winH/2-$(id).height()/2);
        $(id).css(‘left’, winW/2-$(id).width()/2);
        //transition effect
        $(id).fadeIn(2000);
    });
    //if close button is clicked
    $(‘.window .close’).click(function (e) {
        //Cancel the link behavior
        e.preventDefault();
        $(‘#mask, .window’).hide();
    });       
    //if mask is clicked
    $(‘#mask’).click(function () {
        $(this).hide();
        $(‘.window’).hide();
    });           
});

</script>

</head>

<body>

<!– #dialog is the id of a DIV defined in the code below –>
<a href="#dialog" name="modal"><b>Get List Data</b></a>

<div id="boxes">

    <!– #customize your modal window here –>

    <div id="dialog" class="window">
        <b>Get List Data</b> |
        <table id="test" border=0 cellspacing=0 cellpadding=0 tabindex=1>
              <thead>
            <tr><th>Header1</th><th>Header2</th><th>Header3</th></tr>
              </thead>
              <tfoot>
            <tr><th>Foot1</th><th>Foot2</th><th>Foot3</th></tr>
              </tfoot>
              <tbody>
              </tbody>
                </table>
        <!– close button is defined as close class –>
        <a href="#" class="close">Close</a>

    </div>

    <!– Do not remove div#mask, because you’ll need it to fill the whole screen –>   
     <div id="mask"></div>
</div>

</body>

</html>

Using the SharePoint SOAP API

Its time to finally call our SharePoint server using the SP library from http://darrenjohnstone.net/2008/07/22/examples-for-the-sharepoint-and-office-live-javascript-api/ . This represents the final bits of code for this post. Again, we cheated a little bit and loaded the list data on page entry. Not a good practice for large lists but that’s not really the point of this post. I add the following script links

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/SPAPI/SPAPI_Core.js"></script>
<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/SPAPI/SPAPI_Lists.js"></script>

The actual call to the service looks as follows

var lists = new SPAPI_Lists(‘http://team.currier.com’)
var items = lists.getListItems(‘Rooms’);

var myListRows;

if (items.status == 200)
{

        myListRows = items.responseXML.getElementsByTagName(‘z:row’);

}

A sample returned row looks as follows

<z:row xmlns:z="#RowsetSchema" ows_ID="1" ows_Attachments="0" ows_LinkTitle="Room 1" ows_Rooms="Room 1" ows_Building="Building A" ows_MetaInfo="1;#" ows__ModerationStatus="0" ows__Level="1" ows_Title="Room 1" ows_owshiddenversion="3" ows_UniqueId="1;#{C805E01D-2F50-45A3-8712-50F0E52DB7F0}" ows_FSObjType="1;#0" ows_Created_x0020_Date="1;#2009-03-29 19:29:47" ows_Created="2009-03-29 19:29:47" ows_FileLeafRef="1;#1_.000" ows_FileRef="1;#Lists/Rooms/1_.000"/>

The code we use to parse this and place into the grid is equally simple.

var rows=[];

for(var i=0;i<myListRows.length;i++) {

           var item = myListRows[i];

           rows.push([item.getAttribute(‘ows_ID’),item.getAttribute(‘ows_Rooms’),item.getAttribute(‘ows_Building’)]);

}
t.addRow(rows);

And the final result looks as follows. Very cool, but I appear to be missing a row (debug for another time)

clip_image001[8]

Here is the actual site list one more time

clip_image001[10]

Finally, the code that runs this demo is here. Many improvements can be made of course.

<html>

<head>

<title>SPClient</title>

<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/jquery-1.3.2.min.js"></script>
<script src="http://team.currier.com/_layouts/jquery/jquery.grid.core.js" type="text/javascript"></script>
<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/SPAPI/SPAPI_Core.js"></script>
<script type="text/javascript" src="http://team.currier.com/_layouts/jquery/SPAPI/SPAPI_Lists.js"></script>

<style>
    .selected{background-color:limegreen !important;}
    .activated{background-color:tomato !important;}
    .even {background-color:#efefef}
    .odd {background-color:#cde}
    #test thead tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test tfoot tr  {       
        background-color: #EBEADB !important;
        border: 1px solid #CBC7B8 !important;
        text-align: left !important;
        font-weight: normal !important;
    }
    #test{
        border-left: 1px solid #CBC7B8;
        border-right: 1px solid #CBC7B8;
    }

        /* Z-index of #mask must lower than #boxes .window */
        #mask {
                 position:absolute;
                 z-index:9000;
                 background-color:#FFF;
                 display:none;
               }

        #boxes .window {
                 position:absolute;
                 width:440px;
                 height:200px;
                 display:none;
                 z-index:9999;
                 padding:20px;
               }

        /* Customize your modal window here, you can add background image too */
        #boxes #dialog {
               width:375px;
               height:203px;
               border-style:solid;
               border-width:5px;
               background-color: #DDDDDD
              }

</style>

<script type="text/javascript">

$(document).ready(function() {
var t=$("#test");
        t.grid({           
            navigate:{
                maintainSelection:false       
            },
            scroll:{
                width:"600",
                height:"300",
                colWidths:[200,200,200]
            },
            stripe:true,
            columnResize:true
        });   

            var lists = new SPAPI_Lists(‘http://team.currier.com’)
            var items = lists.getListItems(‘Rooms’);

            var myListRows;

            if (items.status == 200)
            {

              myListRows = items.responseXML.getElementsByTagName(‘z:row’);
            }

            var rows=[];

            for(var i=0;i<myListRows.length;i++) {
                var item = myListRows[i];
        rows.push([item.getAttribute(‘ows_ID’),item.getAttribute(‘ows_Rooms’),item.getAttribute(‘ows_Building’)]);
            }
            t.addRow(rows);

    //select all the a tag with name equal to modal

    $(‘a[name=modal]’).click(function(e) {
        //Cancel the link behavior
        e.preventDefault();
        //Get the A tag
        var id = $(this).attr(‘href’);
        //Get the screen height and width
        var maskHeight = $(document).height();
        var maskWidth = $(window).width();
        //Set height and width to mask to fill up the whole screen
        $(‘#mask’).css({’width’:maskWidth,’height’:maskHeight});
        //transition effect       
        $(‘#mask’).fadeIn(1000);   
        $(‘#mask’).fadeTo("slow",0.8);   
        //Get the window height and width
        var winH = $(window).height();
        var winW = $(window).width();
        //Set the popup window to center
        $(id).css(‘top’,  winH/2-$(id).height()/2);
        $(id).css(‘left’, winW/2-$(id).width()/2);
        //transition effect
        $(id).fadeIn(2000);
    });
    //if close button is clicked
    $(‘.window .close’).click(function (e) {
        //Cancel the link behavior
        e.preventDefault();
        $(‘#mask, .window’).hide();
    });       
    //if mask is clicked
    $(‘#mask’).click(function () {
        $(this).hide();
        $(‘.window’).hide();
    });           
});

</script>

</head>

<body>

<!– #dialog is the id of a DIV defined in the code below –>
<a href="#dialog" name="modal"><b>Get List Data</b></a>

<div id="boxes">

    <!– #customize your modal window here –>

    <div id="dialog" class="window">
        <b>Get List Data</b> |
        <table id="test" border=0 cellspacing=0 cellpadding=0 tabindex=1>
              <thead>
            <tr><th>ID</th><th>Rooms</th><th>Building</th></tr>
              </thead>
              <tfoot>
            <tr><th>Foot1</th><th>Foot2</th><th>Foot3</th></tr>
              </tfoot>
              <tbody>
              </tbody>
                </table>
        <!– close button is defined as close class –>
        <a href="#" class="close">Close</a>

    </div>

    <!– Do not remove div#mask, because you’ll need it to fill the whole screen –>   
     <div id="mask"></div>
</div>

</body>

</html>

Summary and Related Posts

This was a mouthful but I’m sure you’ll agree offers another degree of flexibility for crafting simple extensions to SharePoint. A word on authentication – these samples really assume windows integrated authentication when dealing with JavaScript and HTML. Don’t attempt to ever pass embedded credentials using these methods. Its assumed these pages and code are served up from SharePoint. Keep in mind that JQuery code can be added to the CEWP. Perhaps in a future post I’ll clean this code up a bit and actually add it into SharePoint in a meaningful way.

My next post in this series will be on leveraging REST

Cheers!  

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s