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