Depuis la sortie de la version RTM du framework ASP .NET AJAX il y a quelques semaines, j’ai cherché comment faire pour intégrer le potentiel offert par ce framework dans des sites Windows SharePoint Services 3.0. Après quelques jours de tests, Alléluia, le miracle s’est produit.
Je me propose donc, aujourd’hui, de partager avec vous le résultat de cette étude et de vous fournir les clés qui vous permettront d’intégrer des « UpdatePanels » et autres nouveautés du framework ASP .NET AJAX dans WSS 3.0, soit directement dans des pages .aspx, soit dans des WebParts.
Avant toute chose, la première étape à l’utilisation du framework ASP .NET AJAX, c’est d’installer ce dernier sur le serveur hébergeant WSS 3.0. Vous pouvez télécharger le framework AJAX depuis l’url suivante :
http://www.microsoft.com/downloads/details.aspx?FamilyID=ca9d90fa-e8c9-42e3-aa19-08e2c027f5d6&displaylang=en
La plateforme que j’utilise dans le cadre de cet article est un serveur Windows Server 2003 US avec également une version US de WSS 3.0. Dans cet environnement, le répertoire d’installation par défaut de WSS est « C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12 ». Si vous travaillez sur une version FR de Windows Serveur 2003, le répertoire est « C:\Program Files\Fichiers Communs\Microsoft Shared\Web Server Extensions\12 ».
Un des répertoires auquel je ferai référence dans cet article est le répertoire contenant les fichiers javascript de WSS 3.0. Sur ma plateforme, ce répertoire est le sous répertoire « Template\Layouts\1033\ » du répertoire d’installation de WSS. Sur une installation FR de WSS, ce répertoire est « Template\Layouts\1036\ ». Pour simplifier la compréhension de l’article, je ferai par la suite référence à ce répertoire en le nommant « répertoire des scripts de WSS ».
J’ai également à ma disposition sur mon serveur une installation complète de Visual Studio 2005 Team Suite et de SharePoint Designer 2007.
Bon, tout le monde est prêt ? Alors, c’est parti …
Je commence tout d’abord par créer, à l’aide de la console d’administration de WSS, une nouvelle Web Application WSS avec une collection de site basée sur le modèle « Site vide ». Voici ci-dessous à quoi ressemble la page d’accueil de ce site :
La prochaine étape va être maintenant de modifier le fichier de configuration de mon site web WSS pour ajouter toutes les informations nécessaires au framework ASP .NET AJAX pour fonctionner.
Pour ceux d’entre vous qui se sont déjà penchés sur le framework ASP .NET AJAX, vous avez sûrement dû remarquer qu’il est nécessaire d’ajouter de nombreuses informations dans le fichier de configuration d’un site ASP .NET standard pour qu’il puisse faire fonctionner de l’AJAX.
La copie d’écran ci-dessous présente, pour ceux d’entre vous qui ne connaissent pas, à quoi ressemble le fichier web.config d’un site ASP .NET AJAX par défaut.

Bon, je sais que ça n’est pas super lisible, mais là n’est pas l’objectif. Le but est surtout de vous faire comprendre que la tâche n’est pas forcément aisée. Vous trouverez dans les sources de l’article un fichier « Ajax, default web.config » qui est une version lisible de la copie d’écran ci-dessus … :D
Bref ! Vous avez donc compris ce qui nous attend. La seconde étape est donc de venir fusionner le fichier de configuration de notre site WSS avec le fichier de configuration par défaut d’un site ASP .NET AJAX. Toujours dans les sources de l’article, vous trouverez les fichiers « Wss, original web.config » et « Wss, final web.config » qui sont, respectivement, le fichier de configuration d’origine de mon site WSS et le fichier de configuration WSS fusionné avec le fichier de configuration AJAX. Pour ce faire, nous allons ouvrir nos deux fichiers de configuration (celui par défaut d’AJAX et celui de notre site WSS) et procéder comme suit :
· Depuis le fichier de configuration AJAX, dans la section configuration\configSections, copiez la section sectionGroup complète et collez-la dans la section configuration\configSections du fichier de configuration de WSS.
· Depuis le fichier de configuration AJAX, dans la section configuration\system.web\pages, copiez la section controls complète et collez-la dans la section configuration\system.web\pages du fichier de configuration de WSS.
· Depuis le fichier de configuration AJAX, dans la section configuration\system.web\compilation\assemblies, copiez toutes les sections add et collez les dans la section configuration\system.web\compilation\assemblies du fichier de configuration de WSS. Attention, une des lignes copiées (déclaration de l’assemby Microsoft.SharePoint) existe déjà dans le fichier de configuration de WSS. Il faut donc la supprimer des lignes copiées.
· Depuis le fichier de configuration AJAX, dans la section configuration\system.web\httpHandlers, copiez toutes les sections add et collez-les dans la section configuration\system.web\compilation\httpHandlers du fichier de configuration de WSS. Attention : les lignes doivent être insérées après la ligne remove.
· Depuis le fichier de configuration AJAX, dans la section configuration\system.web\httpModules, copiez la ligne add et collez-la dans la section configuration\system.web\compilation\httpModules du fichier de configuration de WSS. Attention : la ligne doit être insérée après la ligne clear.
· Depuis le fichier de configuration AJAX, dans la section configuration, copiez la section system.webServer complète et collez-la dans la section configuration du fichier de configuration de WSS.
Voilà, c’est fait. Maintenant, il faut nous assurer que notre site WSS fonctionne toujours correctement. Pour cela, enregistrez les modifications faites sur le fichier de configuration du site WSS. Pour s’assurer que les modifications soient bien prises en compte, exécutez la commande IISRESET. Enfin, connectez-vous à votre site WSS … normalement, tout doit bien fonctionner. En cas d’erreur, vérifiez que le fichier de configuration est bien correct, et que vous n’avez pas commis d’erreur dans le copier-coller.
OK, notre site WSS est configuré pour pouvoir exploiter le framework ASP .NET AJAX. Maintenant, il nous faut mettre en œuvre de l’AJAX dans notre site WSS. La première chose à faire est donc de mettre en place un contrôle AJAX ScriptManager. En effet, toute page web souhaitant exploiter les contrôles AJAX doit contenir un contrôle ScriptManager.
Afin de me simplifier la tâche, et n’avoir à gérer la présence du ScriptManager qu’une seule fois, j’ai décidé de le rajouter directement dans la master page par défaut de mon site WSS. Comment faire ? C’est très simple, vous allez voir.
A l’aide de SharePoint Designer 2007, ouvrez votre site WSS. Ensuite, dans le répertoire _catalogs\masterpage, éditez la page default.master et placez-vous dans le code de cette page comme dans la copie d’écran ci-dessous.
De la même façon que la masterpage déclare un WebPartManager nécessaire à la gestion des WebParts, nous allons déclarer notre ScriptManager. Pour cela, ajoutons la ligne suivante juste au-dessus de la déclaration du WebPartManager :
<asp:ScriptManager ID="mainScriptManager" runat="server"/>
Vous devriez obtenir ceci :
Enregistrez les modifications et reconnectez-vous à votre site WSS pour tester que tout fonctionne. Hélas… non, vous obtenez une erreur WSS vous indiquant que le contrôle System.Web.UI.ScriptManager n’est pas « Safe ».
Comment corriger ce problème ? Là encore, rien de bien compliqué. Il nous suffit d’indiquer dans le fichier de configuration de notre site web que notre assembly System.Web.Extension (c’est l’assembly AJAX qui déclare le contrôle ScriptManager) est safe. Pour cela, éditons donc notre fichier web.config et ajoutons, dans la section configuration/SharePoint/SafeControls la ligne suivante :
<SafeControl Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="System.Web.UI" TypeName="*" Safe="True" AllowRemoteDesigner="True" />
Enregistrez les modifications, un petit coup de IISRESET et re-testez votre site WSS. Le problème est bien corrigé. Nous pouvons passer à la suite…
Nous pouvons maintenant créer un WebPart qui va utiliser les contrôles AJAX. Le web part que je vais développer ici n’a rien d’extraordinaire. Tout ce qu’il fait, c’est mettre à jour l’heure, mais sans rafraîchir toute la page. Ceci devrait nous permettre de vérifier que tout fonctionne correctement.
Je crée donc à l’aide de Visual Studio un nouveau projet de type Class Library. Ensuite, je vais ajouter les références aux assemblies :
· Microsoft.SharePoint (WSS)
· System.Web.Extensions (Contrôles AJAX)
· System.Web (Contrôles ASP .NET Standard)
Je crée ensuite une classe que je nomme WebPartHeure et que je fais hériter de la classe Microsoft.SharePoint.WebPartPages.WebPart. Je rajoute en entête de ma classe la directive using System.Web.UI et using System.Web.UI.WebControls. Mon WebPart va se constituer d’un label chargé d’afficher l’heure et d’un bouton permettant de mettre à jour l’heure. Afin de pouvoir faire le tout en AJAX, ces 2 contrôles seront inclus dans un UpdatePanel. Voici ci-dessous le code complet de WebPart.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Bewise.Wss3.Sample.AjaxEtWss
{
public class WebPartHeure :
Microsoft.SharePoint.WebPartPages.WebPart
{
private Label hour;
private Button refreshHour;
private UpdatePanel updatePanel;
private AsyncPostBackTrigger refreshHourTrigger;
protected override void CreateChildControls()
{
// Label
hour = new Label();
hour.ID = "hour";
// Show datetime
hour.Text = DateTime.Now.ToString();
// Button
refreshHour = new Button();
refreshHour.ID = "refreshHour";
refreshHour.Text = "Refresh !";
// Trigger
refreshHourTrigger = new AsyncPostBackTrigger();
refreshHourTrigger.ControlID = refreshHour.ID;
refreshHourTrigger.EventName = "Click";
// UpdatePanel
updatePanel = new UpdatePanel();
updatePanel.ID = "updatePanel";
updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
// Add trigger to UpdatePanel
updatePanel.Triggers.Add(refreshHourTrigger);
// Add Childs controls
updatePanel.ContentTemplateContainer.Controls.Add(hour);
updatePanel.ContentTemplateContainer.Controls.Add(refreshHour);
// Add all controls to te WebParts
this.Controls.Add(updatePanel);
}
protected override void Render(HtmlTextWriter writer)
{
// Ensure childs controls are created
this.EnsureChildControls();
// Render updatePanel and childs controls
updatePanel.RenderControl(writer);
}
}
}
N’oubliez pas de signer votre assembly sinon, vous ne pourrez pas la référencer dans WSS. Ensuite, créons un fichier .dwp pour pouvoir intégrer notre WebPart dans WSS. Voici le contenu de fichier (pour mon exemple).
<?xml version="1.0" encoding="utf-8" ?>
<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
<Assembly>Bewise.Wss3.Sample.AjaxEtWss, Version=1.0.0.0, Culture=neutral, PublicKeyToken=995401eda3d8bd0c</Assembly>
<TypeName>Bewise.Wss3.Sample.AjaxEtWss.WebPartHeure</TypeName>
<Title>WebPartHeure AJAX</Title>
<Description>Show hour and refresh using AJAX</Description>
</WebPart>
Maintenant que notre WebPart est près, il nous faut encore l’enregistrer dans WSS pour pouvoir le tester. Copiez le fichier .dwp ainsi que votre assembly dans le répertoire bin de votre site web WSS. Ceci fait, éditons le fichier de configuration du site pour déclarer notre assembly comme « Safe ». Pour ce faire, il faut ajouter la ligne suivante dans la section configuration/SharePoint/SafeControls puis enregistrer les modifications.
<SafeControl Assembly="Bewise.Wss3.Sample.AjaxEtWss, Version=1.0.0.0, Culture=neutral, PublicKeyToken=995401eda3d8bd0c" Namespace="Bewise.Wss3.Sample.AjaxEtWss" TypeName="*" Safe="True" AllowRemoteDesigner="True" />
Ensuite, ouvrez votre site WSS. Allons dans la galerie de WebPart du site et téléchargez-y votre fichier .dwp. Vous devriez le voir apparaître dans la liste des WebParts du site comme suit :
Retournez sur la page d’accueil du site et éditez-la. Ajoutez dans une des WebPart Zones disponibles une instance de votre WebPart. Enfin, quittez le mode édition. Vous devriez obtenir quelque chose de similaire.
Testons si tout marche bien… Hélas, et 100 fois hélas ! Si vous testez correctement, vous pouvez vous rendre compte que la première fois qu’on clique sur notre bouton « Refresh », tout se passe bien mais ensuite… plus rien. Pourquoi ?
Qu’est-ce qui peut bien se passer pour que mon WebPart fonctionne correctement la première fois et pas du tout les fois suivantes ? Après avoir passé plusieurs heures à chercher l’origine du problème, j’en ai finalement trouvé la source. Si on réédite à l’aide de SharePoint Designer la master page, on peut observer les 2 lignes suivantes :
<BODY scroll="yes" onload="javascript:if (typeof(_spBodyOnLoadWrapper) != 'undefined') _spBodyOnLoadWrapper();">
et
<form runat="server" onsubmit="return _spFormOnSubmitWrapper();">
La seconde ligne signifie qu’à chaque submit d’une page WSS, la méthode javascript _spFormOnSubmitWrapper() est appelée. Cette méthode est déclarée dans le fichier INIT.JS du répertoire des scripts de WSS (cf. Introduction pour localiser ce répertoire). Voici le code de cette fonction :
function _spFormOnSubmitWrapper()
{
if (_spSuppressFormOnSubmitWrapper) {
return true;
}
if (_spFormOnSubmitCalled) {
return false;
}
if (typeof(_spFormOnSubmit)=="function") {
var retval=_spFormOnSubmit();
var testval=false;
if (typeof(retval)==typeof(testval) && retval==testval) {
return false;
}
}
RestoreToOriginalFormAction();
_spFormOnSubmitCalled=true;
return true;
}
Mais que fait donc cette fonction ? En plus de retourner true ou false en fonction de certaines conditions, on peut se rendre compte que la fonction met à jour la variable _spFormOnSubmitCalled à true. En cherchant encore, on trouve que cette même variable est réinitialisée à false dans la méthode _spBodyOnLoadWrapper() et que cette même méthode est appelée à chaque Load d’une page WSS (cf. première ligne). Mais quel est le problème ?
C’est simple ! En temps normal, sans AJAX, au chargement de la page, la variable _spFormOnSubmitCalled est initialisée à false par la méthode _spBodyOnLoadWrapper(). Ensuite, lors d’un postback, cette variable passe à true (méthode _spFormOnSubmitWrapper()) mais est réinitialisée à false lors du rechargement de la page (comme lors du premier chargement). Maintenant, pourquoi cela ne marche pas quand on utilise de l’AJAX. Là encore, rien de bien compliqué : au premier chargement de la page, la variable est bien positionnée à false. Ensuite, lorsqu’on clique sur notre bouton « Refresh », la méthode _spFormOnSubmitWrapper() positionne la variable à true. Par contre, la différence avec un postback normal, c’est que, à la fin du traitement AJAX, la méthode _spBodyOnLoadWrapper() n’est pas rappelée puisque la page n’est pas entièrement rechargée, donc, notre variable n’est pas repositionnée à false.
Et oui, tout ça à cause d’une simple variable …
Pour corriger le problème, il va nous falloir faire en sorte de laisser la variable spFormOnSubmitCalled à false s’il s’agit d’un submit « AJAX ». Nous allons donc ajouter la prise en compte du cas AJAX dans la méthode spFormOnSubmitWrapper().Voici ci-dessous le nouveau code de la fonction avec, en italique, les modifications apportées. Par sécurité et afin de pouvoir revenir en arrière en cas de problème, je vous conseille vivement de faire une copie du fichier INIT.JS avant toute modification.
function _spFormOnSubmitWrapper()
{
if (_spSuppressFormOnSubmitWrapper) {
return true;
}
if (_spFormOnSubmitCalled) {
return false;
}
if (typeof(_spFormOnSubmit)=="function") {
var retval=_spFormOnSubmit();
var testval=false;
if (typeof(retval)==typeof(testval) && retval==testval) {
return false;
}
}
if((typeof(Sys) != 'undefined' ) &&
(typeof(Sys.WebForms) != 'undefined' ) &&
(Sys.WebForms.PageRequestManager.getInstance() != null) &&
(Sys.WebForms.PageRequestManager.getInstance()._postBackSettings.panelID != ''))
_spFormOnSubmitCalled=false;
else
_spFormOnSubmitCalled=true;
RestoreToOriginalFormAction();
_spFormOnSubmitCalled=true;
return true;
}
Que fait le code que nous avons rajouté ? Il regarde :
· si les types « Sys » et « Sys.WebForm » propres au framework AJAX sont définis,
· s’il existe une instance du ScriptManager (obligatoire pour faire de l’AJAX),
· si l’Id du panel ayant fait le submit est bien enregistré auprès du scriptManager (dans ce cas, le submit a été fait par un contrôle AJAX).
Si toutes ces conditions sont réunies, cela signifie que c’est bien un contrôle AJAX qui a fait le submit. Dans ce cas, nous positionnons la variable à l’origine du problème à false, sinon, on laisse le traitement par défaut.
Et voilà, après ça, tout devrait marcher parfaitement. Afin de vous rassurer sur les modifications faites au niveau du fichier INIT.JS, la variable incriminée n’est utilisée que par les 2 fonctions citées précédemment et nulle part ailleurs.
Bon, effectivement, tout a l’air de bien fonctionner, mais vérifions quand même. La copie d’écran ci-dessous montre la taille de la page default.aspx au premier chargement. On peut même vérifier sur la partie droite de l’écran que le serveur retourne bien la page en entier
Maintenant, la réponse du serveur après avoir cliqué sur notre bouton « refresh »
On peut effectivement constater que la taille de la réponse est bien moins grosse et que le serveur ne retourne pas toute la page.
Ben, rien de particulier à dire. J’espère que cet article va vous dépanner et vous permettre d’enrichir vos applications WSS. N’hésitez pas à me faire part de vos remarques et commentaires.
Amusez-vous bien et à bientôt pour un prochain article.