1,504 articles and 10,427 comments as of Wednesday, April 21st, 2010

Wednesday, October 28, 2009

A jQuery Library for SharePoint Web Services (WSS 3.0 and MOSS): Part 2 – How Does It Work?

Web ServicesIn my last post, I wrote about why I decided to start building the jQuery Library for SharePoint Web Services. In this post, I’ll tell you a bit about what’s there and how it works. If you’re totally familiar with the SharePoint Web Services, this may all be old hat to you, but I felt it would be useful to explain some of the underlying concepts in what I think of as “English” for those who might get something out of it.

As I’ve mentioned, the fundamental idea for the library was to “wrap” the key SharePoint Web Services which would allow you to talk to SharePoint in real time without full page refreshes. As I looked across the Web Services, I saw clear patterns in how they were called (and some glaring exceptions). The patterns meant that I could do some nice generalizing in my jQuery code. The “core” of the jQuery Library for SharePoint Web Services is the $().SPServices function, which allows you to simply call each Web Service operation.

There is a set of Web Services which are available in WSS 3.0 and MOSS, and then some that are only available in MOSS. The table below shows the Web Services currently available in the library and where they work, along with links to the MSDN documentation:

I haven’t tried to wrap every single Web Service or every single operation of each. I’ve focused on the Web Services and operations that seemed as if they would provide the most benefit the most often. If I’ve missed something that would be of use to you, then by all means let me know by dropping a comment into the Discussions on the Codeplex site. I’d be happy to add it in the next release.

Each Web Service is called with a Simple Object Access Protocol (SOAP) Envelope which contains information about what you want to accomplish in the SOAP Body. As an example, here’s what the SOAP call for the Lists Web Service’s GetList operation looks like:

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

You can see what the SOAP call for any of the SharePoint Web Services needs to look like by going to /_vti_bin/[WebService].asmx. You can tack this onto the end of any URL within SharePoint. Here are a couple of examples:

http://servername/_vti_bin/lists.asmx
http://servername/sitepath/_vti_bin/workflow.asmx

On those pages, you’ll see what operations are available, and clicking on any of the operation links will show you the SOAP request and response structures. I’ve stared at most of these a lot so that you don’t really have to.

Building the SOAP Request

The SOAP Envelope and SOAP Body “wrappers” are the same for all of the SharePoint Web Services. Think of this wrapper as saying “I’m going to speak SOAP to you.” In my library, I store these wrappers in the SOAPEnvelope.header and SOAPEnvelope.footer variables:

SOAPEnvelope.header = "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body>";
SOAPEnvelope.footer = "</soap:Body></soap:Envelope>";

Next comes what I call the SOAPEnvelope.opheader, or Operation Header. The Operation Header varies based on what operation you want to use. For the most part, the structure of the Operation Header is consistent for all of the operations within a Web Service, but there are a few exceptions. (Naughty developers.) The Operation Header tells the specific Web Service what operation you want to have a chat with.

We also need to have a SOAPAction, which is passed ahead of the request. In the case of the GetList operation above, it looks like this:

http://schemas.microsoft.com/sharepoint/soap/GetList

The SOAPAction is also generally consistent within a Web Service, but not always. (Naughty developers again.)

Finally, we get to what I call the SOAPEnvelope.payload. This is where you pass in the real data that you want to use to talk to the Web Service. In the GetList example above, the payload simply contains the listName, which can be either the text name of the list (e.g., States) or the GUID for the list (e.g., 8BADF8E2-3758-4D1B-9966-045F4A77F73D).

The AJAX Call

Based on what operation you pass into the SPServices function of the library, it builds up the appropriate SOAP call and uses it “talk” to the Web Service using AJAX (shorthand for Asynchronous JavaScript and XML). The AJAX call is amazingly simple:

$.ajax({
  url: ajaxURL, // The relative URL for the  AJAX call
  async: opt.async, // By default, the AJAX calls are  asynchronous. You can specify false to  require a synchronous call.
  beforeSend: function  (xhr) { // Before sending the msg, need  to send the request header with the SOAPAction
    xhr.setRequestHeader("SOAPAction",  SOAPAction);
  },
  type: "POST", // This is a POST
  data: msg, // Here is the SOAP request  we've built above
  dataType:  "xml", // We're  sending XML
  contentType:  "text/xml; charset='utf-8'", //  and this is its content type
  complete:  opt.completefunc // When the call is  complete, do this
});

Calling the Library

To set things up to use the jQuery Library for SharePoint Web Services, you’ll need to add references to it and the core jQuery library in the right context for what you are trying to accomplish. This may mean that you add the references in your master page (if you are going to use jQuery pervasively), in a page layout (if only a certain class of pages will utilize jQuery), or in specific pages (if you want to use some of the functions only on a list’s forms, for example). These references will look something like this:

<script language="javascript" type="text/javascript" src="/jQuery%20Libraries/jquery-1.3.2.min.js"></script>
<script language="javascript" type="text/javascript" src="/jQuery%20Libraries/jquery.SPServices-0.4.1.min.js"></script>

I recommend putting both .js files into a Document Library in the root of your Site Collection called something like “jQuery Libraries”, as above. This way, you can manage them as content. For deployment purposes, you may want to use a generic name for the current version you are using so that you won’t need to update every reference every time you upgrade the .js files:

<script language="javascript" type="text/javascript" src="/jQuery%20Libraries/jquery-latest.min.js"></script>
<script language="javascript" type="text/javascript" src="/jQuery%20Libraries/jquery.SPServices-latest.min.js"></script>

What all this adds up to is that you can use the jQuery Library for SharePoint Web Services to call any of the Web Service operations it wraps as simply as this:

$().SPServices({
  operation:  "GetList",
  listName:  "States",
  completefunc: function  (xData, Status) {
    ...do something...
  }
});

which is a whole lot easier and efficient than trying to decipher all of the SOAP and AJAX requirements for each operation every time.

You can also pass in some optional options:

  • WebURL — If you pass in a Web URL, then that context is used for the ajaxURL in the AJAX call above. If you don’t pass in a WebURL, the current context is used. (The current context is determined by a call to the WebUrlFromPageUrl operation of the Webs Web Service. This is exposed as the public function $().SPServices.SPGetCurrentSite, as it can be a little tricky to determine the current site otherwise.) The WebURL is only useful in the case where the context matters to the result, and this is indicated in the library’s Documentation.
  • async — [true | false], as described above

One important caveat: a lot of people and blog posts will tell you to simply drop jQuery into Content Editor Web Parts (CEWPs). I’m not saying that this is always a bad idea, but it will make keeping track of your jQuery usage pretty difficult. I recommend using SharePoint Designer to put the jQuery into the pages them selves. This allows you to use whatever deployment mechanisms you already have in place for customized (unghosted) pages and also eliminates the possibility that a user will delete or change the jQuery in the CEWPs inadvertently.

I’m working on a function called SPAuditScript which will let you get a report of script usage in CEWPs across a Site Collection. This ought to help with keeping track of things as well as allay some of the fears that IT folks have about non-developers doing developer-like things without any way to track it.

The Results

When you make one of these calls, you get back XML, just as you’ve passed XML into it. As I was building the library, I found that I very often wanted to see what was in that XML in an easy to read format, so I built the $().SPServices.SPDebugXMLHttpResult function. If you’re comfortable with XML and debugging tools, you may never need this function, but I find it useful. If you wanted to use it in the above example, replace the completefunc with this:

completefunc: function (xData, Status) {
  var out =  $().SPServices.SPDebugXMLHttpResult({
    node:  xData.responseXML
  });
  $(divId).html("").append("<b>This  is the output from the GetList operation:</b>" + out);
}

where divId is the ID for a DIV on the calling page where you’d like to place the results. The output from the $().SPServices.SPDebugXMLHttpResult function for GetList will look something like this excerpt:

jQuery Library for SP Web Services

On the other hand, if you’d like to work with the output, you’d probably do something like this:

$().SPServices({
  operation:  "GetList",
  async: false,
  webURL: opt.webURL,
  listName:  opt.listName,
  completefunc:  function(xData, Status) {
      $(xData.responseXML).find("Field").each(function()  {
      if($(this).attr("StaticName")  == opt.columnStaticName) displayName = $(this).attr("DisplayName");
    });
  }
});

This example comes from the $().SPServices.SPGetDisplayFromStatic function. In the completefunc, I find all of the Field elements, iterate through them until I find the one I’m looking for, then grab the DisplayName attribute to return. I’m not going to go into a jQuery tutorial here, but working with the XML SOAP response is often about this simple.

Conclusion

So all of this (hopefully) explains what happens when you use the jQuery Library for SharePoint Web Services function $().SPServices to call a Web Service operation. Any one of these Web Service operations can let you do some spiffy stuff, but to me the real power comes in utilizing one or more operations together to accomplish cool things. For instance, the $().SPServices.SPCascadeDropdowns function uses GetListItem, $().SPServices.SPLookupAddNew uses GetList (2x) and GetFormCollection, and so on.

In my next post, I’ll write about the other functions in the SPServices namespace and how you can use them to improve the user experience in SharePoint.

Marc D AndersonGuest Author: Marc D. Anderson
http://mdasblog.wordpress.com

Marc D. Anderson is a Co-Founder and the President of Sympraxis Consulting LLC, based in Newton, MA.  He has over 25 years of experience as a technology consultant and line manager across a wide spectrum of industries and organizational sizes.  Marc has done extensive consulting on knowledge management and collaboration and what makes them actually work in practice.  Marc is a very frequent “answerer” on the MSDN SharePoint – Design and Customization forum.

 

Please Join the Discussion

6 Responses to “A jQuery Library for SharePoint Web Services (WSS 3.0 and MOSS): Part 2 – How Does It Work?”
  1. Matt Farley says:

    We’ve been using this library for the past few weeks for the SPCascadeDropdown functionality and it’s been fantastic.

  2. Great to hear, Matt. Let me know if you have any problems, and especially if you have suggestions.

    M.

  3. Ben McMann says:

    Is there a way where you can allow multiple selections on a drop down with this solution. For example, your parent column would be Process, your child column would be sub process, and you want to select multiple sub processes?

  4. Ben:

    I assume that you mean SPCascadeDropdowns. That enhancement is on the list, as quite a few people have asked for it. I hope to make the function work such that either or both the parent and child can be multi-selects in the next few weeks. If you Twitter, follow @jQSPWS for updates or watch the Codeplex site for new releases.

    M.

  5. Christophe says:

    Marc – I don’t use the SharePoint Web Services much, and the main reason is that they only work for authenticated users.
    Do you have a solution to make this library work for anonymous users?

  6. Christophe:

    You are correct: to use the Web Services, the user needs to be authenticated on the client side for my library to be of use. I don’t believe that there is any way around this, which is reasonable from a security perspective.

    You can call the Web Services server-side, of course, but that’s not my realm here.

    M.


Notify me of comments to this article: