SharePoint et les technologies 3.5 - Part II (WCF-REST, Json et jQuery)

Stéphane Eyskens

Commentez

Ce tutoriel fait partie de la série de tutoriaux consacrés à SharePoint et les technologies 3.5. Le premier volet était consacré à l'utilisation des technologies AJAX. Dans celui-ci, nous allons voir par le biais d'un exemple simple, comment intégrer un service web WCF "REST-Like" dans SharePoint. Nous en profiterons également pour l'interroger avec jQuery et récupérer les infos du service en Json.

- Titre : SharePoint et les technologies 3.5 - Part II (WCF-REST, Json et jQuery)

- Auteur : Stéphane Eyskens

- Parution : 15 juillet 2009

- Licence : Copyright ® 15/07/2009 Developpez. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. L'exemple en images

- III. Projets à réaliser

- III-A. Le contrat

- III-B. Le service

- III-B-1. Le code du service

- III-B-2. Le .svc et le web.config

- III-C. Le module

- III-D. L'enregistrement du module

- III-E. Le custom field type

- III-E-1. Intégration de jQuery

- III-E-2. Code du customfield type

- III-E-2-a. Code de la classe principale

- III-E-2-b. Code du fichier FLDTYPES....xml

- IV. Déploiement

- V. Remerciements

- VI. Téléchargement

I - Introduction

SharePoint 2007 est nativement basé sur framework .NET 3 qui supportait déjà WCF. La version 3.5 du framework a apporté certaines additions à WCF. Si vous ne connaissez pas WCF, je vous encourage à consulter les tutoriaux suivants : Chattez avec WCF et plus particulièrement Introduction aux services web REST avec WCF 3.5. Ce tutoriel ne porte pas sur WCF proprement dit ni sur REST mais plutôt sur la manière d'intégrer un service WCF dans le contexte de SharePoint.

II - L'exemple en images

Les images ci-dessous illustrent la fonctionnalité qui est développée tout au long de ce tutoriel.

Il s'agit d'un type de colonne personnel (custom field type) qui affiche une icône cliquable, laquelle permet de récupérer toutes les infos inhérentes au document associé en AJAX (jQuery).

En réalité, voici le workflow des opérations se déroulant en coulisses :

Click sur icône => requête AJAX jQuery vers le service WCF REST => Exécution de la requête par le service dans le contexte SharePoint (identité de l'utilisateur) et retour de réponse JSON => Analyse de la réponse JSON via jQuery => Mise à jour du DOM via jQuery

Cet exemple simple (sans énormément de valeur ajoutée je vous le concède :)) nous permettra néanmoins d'analyser tous les aspects liés à la mise en place du service WCF et à la communication entre le client et ce dernier.

III - Projets à réaliser

Voici, projet par projet tout ce qu'il va falloir mettre en place pour arriver à nos fins. Notez qu'une solution contenant tous les projets est disponible en téléchargement.

- DVP.SharePointWCFRestJsonJquery.Contracts : contrat de notre service WCF (1)

- DVP.SharePointWCFRestJsonJquery.WCFService : service WCF (1)

- DVP.SharePointWCFRestJsonJquery.WCFModule : module HTTP permettant l'utilisation de WCF dans SharePoint (2)

- DVP.SharePointWCFRestJsonJquery.SPVirtualPathRegistration : fonctionnalité d'application SharePoint permettant d'enregistrer le module HTTP dans le web.config de l'application ciblée (2)

- DVP.SharePointWCFRestJsonJquery.SPCustomField : type de colonne personnel qui appelle le service WCF pour afficher dynamiquement les propriétés d'un document

(1) La séparation du contrat WCF du service lui-même n'est pas indispensable mais constitue un bon design pattern dans la mesure où un même contrat peut-être utilisé par différents services. Pour plus d'infos sur la question, vous pouvez vous reporter à la webcast suivante : Miguel Castro:Extreme WCF.

(2) Comme nous le verrons de manière plus détaillée, l'intégration réelle d'un service WCF dans SharePoint n'est pas une sinécure. Il est nécessaire de créer un module HTTP pour enregistrer un VirtualPathProvider afin de supporter les accès aux fichiers .svc.

III-A - Le contrat

Le contrat est extrêmement simple, il s'agit simplement d'offrir la possibilité de retourner les propriétés d'un document. Voici donc la déclaration du contrat :

[ServiceContract]
public interface IDocumentInfo
{
[OperationContract]
[WebGet(UriTemplate = "getdocinfo/{ListName}/{DocumentId}",
ResponseFormat = WebMessageFormat.Json)]
DocumentInfo GetDocumentInfo(string ListName, string DocumentId);

}

Il définit simplement une opération qui répond à toutes les URLs contenant le masque défini dans l'attribut UriTemplate comme par exemple :

- http://.../getdocinfo/une liste/1

- http://.../getdocinfo/guid liste/1

où "une liste" correspond au nom de la bibliothèque SharePoint et "1" à l'ID du document. Notez que le format de réponse est Json (Javascript Object Notation).

En plus du contrat lui-même, il est nécessaire de définir le format des données qui va être renvoyé par le service. Ceci se fait grâce au DataContract suivant :

namespace DVP.SharePointWCFRestJsonJquery.Example.Contracts
{
[DataContract]
public class DocumentInfo
{
[DataMember]
public string DocumentTitle
{
get;
set;
}
[DataMember]
public string DocumentSize
{
get;
set;
}
[DataMember]
public string DocumentCreated
{
get;
set;
}
[DataMember]
public string DocumentModified
{
get;
set;
}
[DataMember]
public string DocumentUrl
{
get;
set;
}
[DataMember]
public string DocumentVersion
{
get;
set;
}
}
}

Ceci représente simplement les propriétés du document qui sera ciblé par l'appel au service web. Notez que la DLL doit être signée et déployée en GAC.

III-B - Le service

Le but de notre service est d'implémenter le contrat que nous venons de définir. Il doit donc simplement implémenter la méthode GetDocumentInfo. Celle-ci reçoit en paramètre le nom de la bibliothèque cible et l'ID du document pour lequel il faut retourner les infos.

III-B-1 - Le code du service

Sahil Malik, MVP SharePoint, a écrit un article complet sur son blog concernant l'intégration de WCF et de SharePoint. Il a été le premier à parler de ce type d'intégration.

Il détaille clairement les points clés pour une intégration réussie. Je vous encourage à consulter son blog régulièrement car il regorge de posts pertinents.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class Service : IDocumentInfo
{
#region IDocumentInfo Members
public DocumentInfo GetDocumentInfo(string ListName, string DocumentId)
{
int ItemId=0;
DocumentInfo ReturnedDocumentInfo = null;
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
try
{
ItemId = Convert.ToInt16(DocumentId);
SPList TargetList=null;
try
{
TargetList = SPContext.Current.Web.Lists[ListName];
}
catch
{
try
{
TargetList = SPContext.Current.Web.Lists[new Guid(ListName)];
}
catch
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
return null;
}
}
SPListItem TargetItem = TargetList.GetItemById(ItemId);
if (TargetItem != null)
{
string TitleField = TargetList.Fields.GetFieldByInternalName("Title").Title;
string SizeField = TargetList.Fields.GetFieldByInternalName("File_x0020_Size").Title;
string CreatedField = TargetList.Fields.GetFieldByInternalName("Created").Title;
string ModifiedField = TargetList.Fields.GetFieldByInternalName("Last_x0020_Modified").Title;
string UrlField = TargetList.Fields.GetFieldByInternalName("EncodedAbsUrl").Title;
string VersionField = TargetList.Fields.GetFieldByInternalName("_UIVersionString").Title;
ReturnedDocumentInfo = new DocumentInfo();
ReturnedDocumentInfo.DocumentSize = (TargetItem[SizeField] != null) ? TargetItem[SizeField].ToString() : "";
ReturnedDocumentInfo.DocumentTitle = (TargetItem[TitleField] != null) ? TargetItem[TitleField].ToString() : "";
ReturnedDocumentInfo.DocumentCreated = (TargetItem[CreatedField] != null) ? TargetItem[CreatedField].ToString() : "";
ReturnedDocumentInfo.DocumentModified = (TargetItem[ModifiedField] != null) ? TargetItem[ModifiedField].ToString() : "";
ReturnedDocumentInfo.DocumentUrl = (TargetItem[UrlField] != null) ? TargetItem[UrlField].ToString() : "";
ReturnedDocumentInfo.DocumentVersion = (TargetItem[VersionField] != null) ? TargetItem[VersionField].ToString() : "";
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
}
}
catch
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;
}
return ReturnedDocumentInfo;
}

#endregion
}

Vous aurez sûrement noté la présence de l'attribut AspNetCompatibilityRequirements. Il permet de rendre le service compatible avec ASP.NET. Ceci est nécessaire pour pouvoir récupérer un httpcontext et un SpContext. Notez qu'en production, il faudrait rajouter quelques contrôles et retourner des messages d'erreurs plus explicites mais j'ai fait le plus light possible pour que cela reste lisible.

III-B-2 - Le .svc et le web.config

Le fichier .svc et le web.config peuvent tous deux être déployés dans le répetoire 12\ISAPI\<répertoire personnel>. Il est également possible de leur créer un home dédié au niveau de c:\inetpub\wwwroot\wss\... mais il est dès lors plus difficile d'intégrer tout cela dans une solution SharePoint. L'objectif est de les rendre accessibles depuis n'importe quel site SharePoint. Le rôle du fichier .svc est d'indiquer au système où se trouve le code-behind du service. Dans notre cas, il s'agit d'une DLL signée et déployée en GAC (voir section déploiement).

Le rôle du web.config est de définir le comportement du service WCF en terme de binding et de protocoles.

Voici le code du fichier .svc déployé dans notre cas dans 12/ISAPI/DVP soit _vti_bin/DVP/ :

<%@ Assembly Name="DVP.SharePointWCFRestJsonJquery.WCFService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=26a6dc14bab9f98b"%>
<% @ServiceHost Service="DVP.SharePointWCFRestJsonJquery.WCFService.Service" %>

et voici celui du web.config également déployé dans 12/ISAPI/DVP soit _vti_bin/DVP/ :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<bindings>
<webHttpBinding>
<binding name="customwebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="DocumentInfoBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="DocumentInfoServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="DocumentInfoServiceBehavior"
name="DVP.SharePointWCFRestJsonJquery.WCFService.Service">
<endpoint address="" bindingConfiguration="customwebHttpBinding" behaviorConfiguration="DocumentInfoBehavior" binding="webHttpBinding"
contract="DVP.SharePointWCFRestJsonJquery.Example.Contracts.IDocumentInfo">
</endpoint>
</service>
</services>
</system.serviceModel>
</configuration>

Voici les éléments principaux du fichier web.config :

aspNetCompatibilityEnabled

Cet attribut rend le service WCF compatible avec ASP.NET. Ceci est impératif pour pouvoir récupérer le contexte de SharePoint (SPContext.Current). Si vous ne souhaitez pas réellement faire évoluer votre service dans le contexte de SharePoint, vous pouvez omettre cet attribut.

customwebHttpBinding

La déclaration d'une configuration d'un custom binding est nécessaire pour permettre la prise en charge des credentials en NTLM et Kerberos. De cette manière, le service n'exige pas que l'application SharePoint associée autorise le mode anonyme.

III-C - Le module

Comme mentionné préalablement et à nouveau découvert par Sahil Malik, un module HTTP doit être créé pour permettre à SharePoint de "reconnaître" les URLs commençant par le caractère ~. En effet, l'URL originale qui sert le .svc commence par le caractère ~, ce qui fait cracher SharePoint. Pour remédier à cela, il faut créer son propre VirtualPathProvider et l'enregistrer via un module.

Voici le code du VirtualPathProvider :

public class WCFVirtualPathProvider : VirtualPathProvider
{
public override string CombineVirtualPaths(string basePath, string relativePath)
{
return Previous.CombineVirtualPaths(basePath, relativePath);
}

public override bool FileExists(string virtualPath)
{
string fixedVirtualPath = virtualPath;
if (virtualPath.StartsWith("~") && virtualPath.EndsWith(".svc"))
{
fixedVirtualPath = virtualPath.Remove(0, 1);
}
return Previous.FileExists(fixedVirtualPath);
}
}

Globalement, VirtualPathProvider retire le caractère ~ de l'URL.

Le module HTTP va permettre de charger ce VirtualPathProvider après son enregistrement (voir section suivante). Voici son code:

public class WCFVirtualPathProviderRegistrationModule : IHttpModule
{
static bool wcfProviderInitialized = false;
static object locker = new object();

public void Init(HttpApplication context)
{
if (!wcfProviderInitialized)
{
lock (locker)
{
if (!wcfProviderInitialized)
{
WCFVirtualPathProvider pathProvider = new WCFVirtualPathProvider();
HostingEnvironment.RegisterVirtualPathProvider(pathProvider);
wcfProviderInitialized = true;
}
}
}
}

public void Dispose()
{
}
}

Le VirtualPathProvider est enregistré une seule fois pour l'application SharePoint associée. Si IIS ou l'application pool associé est redémarré, le module se chargera d'enregistrer à nouveau le VirtualPathProvider.

Notez que la DLL doit être déployée en GAC.

III-D - L'enregistrement du module

Maintenant que nous connaissons l'utilité du module HTTP et du VirtualPathProvider, il reste à les rendre exploitables dans SharePoint. Pour ce faire, il faut en réalité mettre à jour le web.config de l'application SharePoint pour laquelle où souhaite utiliser le service WCF. Une possibilité élégante consiste à développer une fonctionnalité d'application qui peut-être activée via la centrale d'administration et/ou stsadm et qui va modifier le web.config de l'application ciblée.

Il suffit donc de créer un projet SharePoint et d'y créer une feature de webapplication comme ceci :

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
Id="{5DD4372E-4E47-4615-9FBD-4930302BF652}"
Title="WCF Registration"
Description="WCF Registration"
Scope="WebApplication"
ReceiverAssembly="DVP.SharePointWCFRestJsonJquery.SPVirtualPathRegistration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=26a6dc14bab9f98b"
ReceiverClass="DVP.SharePointWCFRestJsonJquery.SPVirtualPathRegistration.WCFRegistration"
>
<ElementManifests/>
</Feature>

La fonctionnalité est associée à un feature receiver qui se charge d'effectuer les modifications nécessaires, voici son code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace DVP.SharePointWCFRestJsonJquery.SPVirtualPathRegistration
{
public class WCFRegistration : SPFeatureReceiver
{

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;
webApplication.WebConfigModifications.Add(CreateHttpModuleModification());
webApplication.WebService.ApplyWebConfigModifications();
webApplication.WebService.Update();
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;
webApplication.WebConfigModifications.Remove(CreateHttpModuleModification());
webApplication.WebService.ApplyWebConfigModifications();
webApplication.WebService.Update();
}
public SPWebConfigModification CreateHttpModuleModification()
{
SPWebConfigModification modification;
string ModName = "add[@name='WCFVirtualPathProviderRegistrationModule']";
string ModXPath = "configuration/system.web/httpModules";
modification = new SPWebConfigModification(ModName, ModXPath);
modification.Owner = "DVP";
modification.Sequence = 0;
modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
modification.Value =
string.Format(@"<add name=""{0}"" type=""{1}, {2}"" />",
"WCFVirtualPathProviderRegistrationModule",
"DVP.SharePointWCFRestJsonJquery.WCFModule.WCFVirtualPathProviderRegistrationModule",
"DVP.SharePointWCFRestJsonJquery.WCFModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=26a6dc14bab9f98b");
return modification;
}
}
}

Notez que j'ai ommis d'inclure les deux autres méthodes obligatoires (install/uninstall) par souci de lisibilité. En gros, le code met simplement à jour le fichier web.config de l'application sur laquelle on active/désactive la fonctionnalité. Il va sans dire qu'il est nécessaire d'avoir les privilèges requis pour activer/désactiver la fonctionnalité via la centrale d'administration ou via stsadm.

La modification consiste à ajouter (ou retirer) la déclaration du module HTTP au web.config en déclarant le type (la classe) et la signature complète de l' assemblage contenant le module.

Notez que cet assemblage doit être déployé en GAC

III-E - Le custom field type

Le custom field type est le composant qui va nous permettre d'interroger notre service WCF et d'analyser les réponses de celui-ci via jQuery.

III-E-1 - Intégration de jQuery

jQuery peut-être intégré de plusieurs manières :

- En le référencant directement depuis google : <script src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js">....mais cette pratique est à déconseiller pour un environnement de production

- En téléchargeant jQuery depuis jquery.com et en copiant le fichier .js dans une bibliothèque de styles (se fait généralement pour tous les types de sites publishing) et ensuite en référençant le .js depuis une page maître afin qu'il soit systématiquement chargé

- En téléchargeant jQuery depuis jquery.com et en copiant le fichier .js dans un dossier en dessous de 12\TEMPLATE\LAYOUTS, de cette manière le fichier pourra toujours être référençé via _layouts/votredossier/jquery.js

Les solutions 2 et 3 se valent, choisir entre l'une ou l'autre relève surtout de la manière dont vous souhaitez gérer et mettre à disposition les artifacts clients.

III-E-2 - Code du customfield type

III-E-2-a - Code de la classe principale

Notre custom field dérive de SPFieldText car nous souhaitons simplement afficher une icône au niveau de son rendu. Nous ne stockerons pas de valeur dans cette colonne, c'est pourquoi on fait en sorte d'exclure la colonne des formulaires d'édition lors de sa création.

namespace DVP.SharePointWCFRestJsonJquery.SPCustomField
{
public class DocumentInfo : SPFieldText
{
public DocumentInfo(SPFieldCollection fields, string fieldName) : base(fields, fieldName) { }
public DocumentInfo(Microsoft.SharePoint.SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName) { }
public override void OnAdded(SPAddFieldOptions op)
{
base.OnAdded(op);
this.ShowInDisplayForm = false;
this.ShowInEditForm = false;
this.ShowInNewForm = false;
this.Update(true);
}
}
}

III-E-2-b - Code du fichier FLDTYPES....xml

C'est dans ce fichier qui sert à décrire le type personnel et optionnellement la manière dont celui-ci doit s'afficher en mode liste que tout va se passer. Pour rappel, nous devons :

- Envoyer une requête au service WCF REST que nous avons développé

- Analyse le retour de celui-ci et effectuer les opérations adéquates pour mettre à jour le DOM

<?xml version="1.0" encoding="utf-8" ?>
<FieldTypes>
<FieldType>
<Field Name="TypeName">DocumentInfo</Field>
<Field Name="TypeDisplayName">DocumentInfo</Field>
<Field Name="TypeShortDescription">DocumentInfo</Field> <!-- (1) -->
<Field Name="ParentType">Text</Field>
<Field Name="FieldTypeClass">
DVP.SharePointWCFRestJsonJquery.SPCustomField.DocumentInfo, DVP.SharePointWCFRestJsonJquery.SPCustomField, Version=1.0.0.0,

Culture=neutral, PublicKeyToken=26a6dc14bab9f98b
</Field>
<Field Name="UserCreatable">TRUE</Field>
<Field Name="ShowInListCreate">TRUE</Field>
<Field Name="ShowInSurveyCreate">TRUE</Field>
<Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
<RenderPattern Name="HeaderPattern">
<HTML>
<![CDATA[
<script type="text/javascript" src="/_layouts/Jquery/jquery.js"></script>  <!-- (2) -->
<script type="text/javascript">
function GetDocumentInfo(DocId) {
CurrentId=DocId;
$.ajax({
url: "]]>
</HTML>
<HttpVDir/><HTML><![CDATA[/_vti_bin/dvp/documentinfo.svc/getdocinfo/]]></HTML>
<ListProperty Select='Name'/>
<HTML>
<![CDATA[/"+DocId,
type: "GET",
dataType: "json",
success: ShowDocInfo,
error: function(XMLHttpRequest, textStatus)
{ alert(textStatus); }
});
}
function ShowDocInfo(json) {
$("#__"+CurrentId).text('');
$("#__"+CurrentId).fadeIn('slow');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>Titre : '+json.DocumentTitle+'</b></div>');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>Taille : '+json.DocumentSize+'</b></div>');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>Version : '+json.DocumentVersion+'</b></div>');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>Créé : '+json.DocumentCreated+'</b></div>');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>Modifié : '+json.DocumentModified+'</b></div>');
$("#__"+CurrentId).append('<div class=ms-toolbar><b>URL : '+json.DocumentUrl+'</b></div>');

}
</script>
]]>
</HTML>
</RenderPattern>
<RenderPattern Name="DisplayPattern">  <!-- (3) -->
<HTML><![CDATA[<table><tr><td><span class="ms-toolbar" id="__]]></HTML>
<Column Name="ID"/>
<HTML><![CDATA["></span></td><td><img onclick="if(this.src.indexOf('RECURSML')!=-1) {GetDocumentInfo(']]></HTML>
<Column Name="ID"/>
<HTML>
<![CDATA[');this.src='_layouts/images/CHECK.GIF';} else {$('#__]]>
</HTML>
<Column Name="ID"/>
<HTML>
<![CDATA[').fadeOut('slow');this.src='_layouts/images/RECURSML.GIF';}" src="_layouts/images/RECURSML.GIF"/></td></tr></table>]]>
</HTML>
</RenderPattern>
</FieldType>
</FieldTypes>

(1) La première partie du fichier décrit le type de colonne et ses différentes propriétés.

(2) La deuxième partie du fichier est le HeaderPattern. le HeaderPattern ne s'exécute qu'une fois par colonne pour toute la liste. C'est donc dans celui-ci que nous déclarons nos fonctions javascript. Au préalable, nous référençons le fichier jquery.js issu de _layouts/jquery/jquery.js. Les fonctions GetDocumentInfo et ShowDocInfo font respectivement appel au service et analysent la réponse retournée. Vous noterez que la fonction ShowDocInfo prend un paramètre json. Celui-ci nous permet de parser le résultat envoyé par le service très facilement en faisant directement appel aux propriétés retournées par le service telles que définies par notre DataContract.

Un exemple de réponse Json qui pourrait être renvoyé par notre service ressemble à ceci :

{"DocumentCreated":"7\/5\/2009 9:56:12 AM","DocumentModified":"7\/5\/2009 10:44:52 AM",
"DocumentSize":"4993","DocumentTitle":"zer","DocumentUrl":"http:\/\/spvm\/sapdms\/docs\/democode.txt",
"DocumentVersion":"1.0"}

(3) Enfin, la troisième partie est le DisplayPattern qui nous permet de spécifier l'affichage du custom field type. Dans notre cas, nous souhaitons afficher un icône qui sur clic appelle la fonction GetDocumentInfo en lui passant en paramètre l'ID du document courant. Ceci se fait aisément grâce à <Column Name="ID"> qui renvoie la valeur de la colonne ID pour la ligne "active". Toujours grâce à cette technique, nous définissons un objet SPAN dont l'ID est dynamiquement déterminé en concaténant la chaîne "#__" à la valeur de l'ID et ce pour chaque ligne affichée par la liste.

IV - Déploiement

Toutes les DLL présentes dans la solution sont déployées en GAC. Par souci de facilité, j'ai utilisé des actions post-build Visual Studio qui ajoute les DLL aux différentes solutions SharePoint. Vous devez ajouter (stsadm -o addsolution -filename <chemin solution>) toutes les solutions SharePoint présentes dans la solution Visual Studio et ensuite les déployer (stsadm ou centrale) afin d'obtenir ceci :

Ensuite, vous devrez activer la fonctionnalité d'application qui effectue les modifications du web.config pour enregistrer le module lié à notre VirtualPathProvider :

et vous serez paré pour utiliser la solution.

V - Remerciements

Je tiens à remercier particulièrement Sahil Malik pour ses contributions qui m'ont permis d'appréhender correctement l'intégration de WCF et SharePoint. Je vous encourage à consulter son blog.

VI - Téléchargement

Vous pouvez télécharger la solution comprenant les 5 projets ici