In SharePoint 2010 è possibile usare il protocollo REST per interrogare una qualsiasi lista. Le funzionalità REST per le liste sono esposte tramite il WCF ListData che si trova in /vti_bin/ListData.svc.

L'esempio in questa pagina è un plugin jQuery che, sfruttando il protocollo REST, permette di cercare una parola o un umero di telefono in una lista di SharePoint di tipo Contacts.
schermata jquery pluginschermata jquery plugin
Per visualizzare i risultati uso la libreria knockout.

I plugin funziona in questo modo: se inserisco una parola che inizia con una lettera (nell'esempio pip) viene costruita la url del servizio REST in questo modo:
http://sharepoint/_vti_bin/ListData.svc/Contacts1?$filter=(LastName ne null and substringof('pip',tolower(LastName))) or (FirstName ne null and substringof('pip',tolower(FirstName)))&$orderby=LastName&$select=LastName,FirstName,BusinessPhone,Path,Id
che permette di eseguire la ricerca sui campi LastName e FirstName.
Se invece la parola iniza con un numero (nell'esempio 555) la ricerca aviene sul campo BusinessPhone e la query diventa:
http://sharepoint/_vti_bin/ListData.svc/Contacts1?$filter=substringof('555',BusinessPhone)&$orderby=LastName&$select=LastName,BusinessPhone,Path,Id

Per utilizzare l'esempio è sufficiente inserire il codice in un file html salvato, ad esempio, in una document library e poi utilizzarlo in una Web Part di tipo Content Editor:
<!-- rimuovere questi link se sono gia' presenti nella master page queste librerie --->
<script type="text/javascript" src="/_layouts/Sgart/knockout-2.1.0.js"></script>
<script type="text/javascript" src="/_layouts/Sgart/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="/_layouts/Sgart/sgartContactSearch.js"></script>
in questo caso suppongo che esista una lista di tipo Contacts chiamata Contacts1:
<!-- view html -->
<div id="sgartContactView">
  <div>
    Contact name: <input type="text" id="sgartContactName" /> 
	<img src="/_layouts/IMAGES/pickerprogressbar.gif" alt="updating..." style="display:none;" 
		 data-bind="visible: updating() == true" />
  </div>
 
  <div id="sgartContactResult" style="border:1px solid gray; display:none;" 
		data-bind="visible: items().length > 0">
      <h2>Contacts</h2>
      <ul data-bind="foreach: items">
          <li><a data-bind="text: name, attr: {href: url}" target="_blank"></a>
          <div data-bind="text: phone"></div></li>
      </ul>
      <div>Items: <span data-bind="text: items().length"></span></div>
      <div>Search query: <span data-bind="text: searchQuery"></span></div>
  </div>
  <div style="color:red; display:none;" data-bind="visible: error().length > 0">
	Error: <span data-bind="text: error"></span>
  </div>
</div>

<!-- initialize plugin-->
<script type="text/javascript">
$(document).ready(function(){
	$("#sgartContactName").sgartContactSearch({
		viewId: "sgartContactView",
		listDisplayName: "Contacts1",
		minLength: 2	
	});
}); 
</script>

questo è il codice del plugin jQuery che permette la ricerca:
File: sgartContactSearch.js
(function ($) {
  //internal
  var properties = {
    searchTimer: null,
    viewModel: null,
    viewModelTemplate: function () {
      var self = this;
      self.error = ko.observable(""); //errori
      self.updating = ko.observable(false); //se true in fase di recupero dati
      self.items = ko.observableArray();   //tutti gli item ritornati dalla query
      self.listTitle = ko.observable("Contacts");
      self.searchQuery = ko.observable("");

      self.addItem = function (sName, sUrl, sPhone) {
        self.items.push({ name: sName, url: sUrl, phone: sPhone });
      }
      self.removeAllItems = function () {
        self.items.removeAll();
      }
      //campo calcolato con il totale degli items
      self.totalItems = ko.computed(function () {
        return self.items.length;
      });
    }
  }

  var methods = {
    search: function (event, options) {
      //recupero le keyword di ricerca
      var name = $(event.currentTarget).val().toLowerCase();
      //resetto gli items nel ViewModel
      properties.viewModel.removeAllItems();
      properties.viewModel.error("");
      //se ho meno di X caratteri non eseguo la ricerca
      if (name.length < options.minLength)
        return;
      properties.viewModel.updating(true);

      //eseguo la chiamata ajax asincrona
      //imposto la query
      var url = "";
      if(options.webUrl != '/'){
        url = options.webUrl;
      }
      var queryUrl = "";
      if (isNaN(name) == false || name[0] == '+') {
        //per i numeri
        queryUrl = "/_vti_bin/ListData.svc/" + options.listDisplayName + "?"
			  + "$filter=substringof('" + name + "',BusinessPhone)"
			  + "&$orderby=LastName"
			  + "&$select=LastName,BusinessPhone,Path,Id";
      } else {
        //per il nome
        queryUrl = "/_vti_bin/ListData.svc/" + options.listDisplayName + "?"
			  + "$filter=(LastName ne null and substringof('" + name + "',tolower(LastName))) or (FirstName ne null and substringof('" + name + "',tolower(FirstName)))"
			  + "&$orderby=LastName"
			  + "&$select=LastName,FirstName,BusinessPhone,Path,Id";
      }
      //solo per debug, visualizzo la query
      properties.viewModel.searchQuery(queryUrl);

      //eseguo la chiamata ajax impostando i dati di ritorno di tipo json
      $.ajax({
        url: queryUrl,
        dataType: 'json',
        success: function (data) {
          $.each(data.d.results, function (key, item) {
            //aggiungo gli item ritornati al View Model
            var url = item.Path + "/DispForm.aspx?ID=" + item.Id;
            var name = (item.FirstName == null || item.FirstName == '' ? '' : item.FirstName + ' ') + (item.LastName == null ? '' : item.LastName);
            properties.viewModel.addItem(name, url, item.BusinessPhone);
          });
          properties.viewModel.updating(false);
        },
        error: function (data) {
          properties.viewModel.error("ERROR:  " + data);
          properties.viewModel.updating(false);
        }
      });
    }
  };

  $.fn.sgartContactSearch = function (options) {
    /* default dei parametri passati in options */
    var defaults = {
      viewId: "sgartContactView", //id del template della view
      listDisplayName: "Contacts", //lista del sito su cui eseguire la ricerca
      searchTimeout: 400, //millisecondi per far partire la ricerca
      minLength: 2 //numero di caratteri minimo per eseguire la ricerca
    };
    //fa il merge dei parametri passati con quelli di default
    var settings = $.extend(defaults, options);

    return this.each(function () {
      var $this = $(this);
      //recupero la View
      var view = document.getElementById(settings.viewId);
      //creo il ViewModel
      properties.viewModel = new properties.viewModelTemplate();
      //imposto la lista su cui cercare
      properties.viewModel.listTitle(settings.listDisplayName);
      //faccio il binding tra la view e il ViewModel
      ko.applyBindings(properties.viewModel, view);
      //aggiungo l'evento keyup alla textbox
      $this.keyup(function (event) {
        //per ogni tasto resetto il timer
        clearTimeout(properties.searchTimer);
        //e lo reimposto a mezzo secondo, allo scadere del tempo parte la ricerca
        properties.searchTimer = setTimeout(function () { methods.search(event, settings) }, settings.searchTimeout);
      });
    });
  }
} (jQuery));  
Alcune note sul protocollo rest.

La url utilizzata è del tipo:
http://sharepoint/_vti_bin/ListData.svc/Contacts1?$filter=substringof('pip',tolower(LastName))&$orderby=LastName&$select=LastName,BusinessPhone,Path,Id
dove:
  • http://sharepoint/_vti_bin/ListData.svc: è il WCF che espone i servizi di interrogazione delle liste
  • Contacts1: è la lista che vogliamo interrogare (attenzione si tratta del DisplayName non della url)
  • $filter: è il filtro da applicare per estrarre i dati (l'equivalente della where in sql), per un elenco degli operatori disponibili vedi http://msdn.microsoft.com/en-us/libr...c907912.aspx
  • $orderby: è l'ordinamento del risultato (si possono usare anche le keyword asc e desc separate da uno spazio)
  • $select: sono i campi che vogliamo farci ritornare
per avere un elenco delle liste esposte dal WCF usare una url del tipo:
http://sharepoint/_vti_bin/ListData.svc
mentre per avere un elenco dei campi che ritorna il WCF usare una url del tipo:
http://sharepoint/_vti_bin/ListData.svc/Contacts1(1)
dove Contacts1 è il nome della lista e 1 è un indice che indica il primo elemento dei dati ritornati, ritornerà un xml tipo questo:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xml:base="http://win-2gomio4u8t9/_vti_bin/listdata.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/"1"" xmlns="http://www.w3.org/2005/Atom">
  <id>http://win-2gomio4u8t9/_vti_bin/listdata.svc/Contacts1(1)</id>
  <title type="text">Pippo</title>
  <updated>2012-10-20T22:58:48+02:00</updated>
  <author>
    <name />
  </author>
  <link rel="edit" title="Contacts1Item" href="Contacts1(1)" />
  <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CreatedBy" type="application/atom+xml;type=entry" title="CreatedBy" href="Contacts1(1)/CreatedBy" />
  <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ModifiedBy" type="application/atom+xml;type=entry" title="ModifiedBy" href="Contacts1(1)/ModifiedBy" />
  <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Attachments" type="application/atom+xml;type=feed" title="Attachments" href="Contacts1(1)/Attachments" />
  <category term="Microsoft.SharePoint.DataService.Contacts1Item" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
  <content type="application/xml">
    <m:properties>
      <d:Id m:type="Edm.Int32">1</d:Id>
      <d:ContentTypeID>0x010600C891E38E85324345B38542FEB55CA6AA</d:ContentTypeID>
      <d:ContentType>Contact</d:ContentType>
      <d:LastName>Pippo</d:LastName>
      <d:Modified m:type="Edm.DateTime">2012-10-20T22:58:48</d:Modified>
      <d:Created m:type="Edm.DateTime">2012-10-20T22:58:48</d:Created>
      <d:CreatedById m:type="Edm.Int32">1</d:CreatedById>
      <d:ModifiedById m:type="Edm.Int32">1</d:ModifiedById>
      <d:Owshiddenversion m:type="Edm.Int32">1</d:Owshiddenversion>
      <d:Version>1.0</d:Version>
      <d:Path>/Lists/Contacts</d:Path>
      <d:FirstName m:null="true" />
      <d:FullName m:null="true" />
      <d:EMailAddress m:null="true" />
      <d:Company m:null="true" />
      <d:JobTitle m:null="true" />
      <d:BusinessPhone>+39 555 123</d:BusinessPhone>
      <d:HomePhone m:null="true" />
      <d:MobileNumber m:null="true" />
      <d:FaxNumber m:null="true" />
      <d:Address m:null="true" />
      <d:City m:null="true" />
      <d:StateProvince m:null="true" />
      <d:ZIPPostalCode m:null="true" />
      <d:CountryRegion m:null="true" />
      <d:WebPage m:null="true" />
      <d:Notes><div></div></d:Notes>
    </m:properties>
  </content>
</entry>
Da questo si può vedere che il nome dei campi non coincide ne con il nome interno del campo ne con il display name. Infatti se prendiamo il campo BusinessPhone ha come display name Business Phone (con lo spazio) e come internal name WhorkPhone.