Wednesday, July 13, 2011

Sharepoint Online for Office 365: People Search using SPServices and jQuery

One of my recent projects involved creating a custom web part for SharePoint Online (part of the Office 365 suite). Unfortunately, one of the limitations of developing for the cloud is that you’re bound to a sandboxed solution environment. This particular project required retrieving data from the User Profile Service using the following SharePoint web service:

1.) Query Web Service

Now, if I were not limited to a sandboxed environment my first approach would be to use the SharePoint object model.  In the sandbox, however, we are limited to the Microsoft.SharePoint.dll and any assemblies which include the attribute: “AllowPartiallyTrustedCallersAttribute” as outlined here:


My solution….. use the SPServices library to make the web service calls from the client side.

SPServices is a jQuery library that wraps SharePoint’s Web Services calls using the .ajax() method behind the scenes, I would recommend to anyone reading this blog post to check out their web site for more information. I found the library very easy to use and their site is full of good documentation.


Before using the SPServices library, you’ll have to download the jquery.SPServices-0.6.1.js and save it to an accessible location (for this example I added “jquery.SPServices-0.6.1.js” to the SharePoint Site Assets folder on my site) and use the following reference:

   1: <script language="javascript" type="text/javascript" src="https://Server_Name/sites/Your_Site_Name/SiteAssets/Scripts/jquery.SPServices-0.6.1.js"></script>


The best thing about SPServices is that it does all of the heavy lifting for you, saving you the time of coding the Ajax calls and constructing the SOAP envelope.

For an outline of the ‘general syntax’ of the SPServices function I used, refer to the following link:


Lets take a look at how I used the SPServices function:

   1: $().SPServices({
   2:     operation: "Query",
   3:     async: false,
   4:     queryXml: query,
   5:     completefunc: function (xData, Status) {
   6:         xmlProfileResponse = xData.responseXML.xml;
   7:     }
   8: });

Every Web Service call will follow a similar structure with different parameters according to the operation.  For example, if you look at line 2 from the snippet above, the ‘operation’ parameter is set to “Query” which indicates to the library that it will be calling the Query Web Service.  On line 5, ‘completefunc’ accepts a reference to the callback function that should be executed upon completion of the Web Service call.  For this example, I’m setting the response XML to a variable, ‘xmlProfileResponse’ and then parsing it later.  Finally, the ‘queryXml’ parameter on  line 4 is where the search criteria is defined. Here is the query itself:

   1: var query = '<?xml version=\"1.0\" encoding=\"utf-8\" ?>'
   2:         + '<QueryPacket xmlns=\"urn:Microsoft.Search.Query\" Revision=\"1000\">'
   3:         + '<Query domain=\"QDomain\">'
   4:         + '<SupportedFormats>'
   5:         + '<Format>urn:Microsoft.Search.Response.Document.Document</Format>'
   6:         + '</SupportedFormats>'
   7:         + '<Context>'
   8:         + '<QueryText language=\"en-US\" type=\"MSSQLFT\">'
   9:         + 'SELECT AccountName, Department, WorkId, Description, PreferredName, LastName, Title, Path, MobilePhone FROM SCOPE() WHERE '
  10:         + "(\"DAV:contentclass\" = 'urn:content-class:SPSPeople') "
  11:                      + " AND (\"LASTNAME\" LIKE '%" + lastName + "%')"
  12:         + '</QueryText>'
  13:         + '</Context>'
  14:         + '</Query>'
  15:         + '</QueryPacket>';

As you can see, it’s a straight forward query. In line 11 above I’m filtering by lastName. Notice that each of the values in the Select clause (line 9) come from the User Profile Service’s default fields. The scope is defined by line 10, SPSPeople.

The next step was parsing the results. I chose to parse on the client side by using jQuery and some simple javascript:

   1: function ParseXML(xml) {
   2:     var nameElement;
   4:     $(xml).find("Properties").each(function () {
   5:         var oPerson = new Person();
   6:         $(xml).find("Property").each(function () {
   7:             nameElement = $(this).find("Name").text();
   8:             if (nameElement == "PREFERREDNAME") {
   9:                 oPerson.preferredName = $(this).find("Value").text();
  10:             } else {
  11:                 if (nameElement == "LASTNAME") {
  12:                     oPerson.lastName = $(this).find("Value").text();
  13:                 } else {
  14:                     if (nameElement == "TITLE") {
  15:                         oPerson.title = $(this).find("Value").text();
  16:                     } else {
  17:                         if (nameElement == "PATH") {
  18:                             oPerson.path = $(this).find("Value").text();
  19:                         } else {
  20:                             if (nameElement == "MOBILEPHONE") {

window.addEventListener('DOMContentLoaded', (event) => {
var curLoc = window.location.href.substr(window.location.href.lastIndexOf('#'));
if (curLoc === "#cookiePolicy") {
$([document.documentElement, document.body]).animate({
scrollTop: ($("#cookiePolicyId").offset().top)-100