SharePoint 2010 : Personnaliser la XSLT List View Web Part

sharepoint-server

Dans la version 2007 de SharePoint, la List View web part proposait la sélection d’une liste et d’un affichage de la liste choisie. Optionnellement, on pouvait modifier la vue pour par exemple afficher une colonne supplémentaire.

Dans SharePoint 2010, cette web part est remplacée par la XSLT List View Web Part. Celle-ci conserve ces fonctionnalités, mais permet en plus de modifier le rendu HTML grâce à une transformation XSL personnalisable. Ce mécanisme permet de redéfinir entièrement le rendu des données issues d’une liste SharePoint, en ayant le contrôle sur la requête CAML sous-jacente. Un vrai couteau suisse :)

Dans ce billet, nous allons voir comment modifier la requête CAML sous-jacente, comment personnaliser la transformation XSLT qui assure le rendu HTML, et comment déporter cette transformation dans un fichier xslt. En effet, par défaut le XSL customisé est stocké en propriété de la web part, mais il est  possible de l’enregistrer dans un fichier séparé afin de le réutiliser pour plusieurs web parts.

Le tout sera fait avec l’interface web classique et SharePoint Designer.

Créons tout d’abord une liste d’annonces, puis ajoutons la web part correspondante sur la page d’accueil.

image

A ce stade, la XSLT List View Web part exploite la transformation XSLT “out of the box”. Voyons comment modifier la requête CAML et le rendu avec SharePoint Designer. Pour cela on sélectionne la web part, puis dans l’onglet “Liste” on demande la modification de l’affichage avec SharePoint Designer, comme sur cette capture :

image  

SharePoint Designer s’ouvre alors en modification sur la page courante afin de nous permettre de modifier la définition de la web part. On constate que la requête CAML sous-jacente est embarquée dans la définition de la web part.

image

Modifions cette requête CAML pour y supprimer les colonnes Attachments et Modified, et ajoutons la colonne Body :

image

On peut alors enregistrer et constater le changement dans le navigateur :

image

Cette possibilité existait déjà avec SharePoint 2007. Voyons maintenant comment modifier le rendu HTML en personnalisant la transformation XSLT, ceci étant une nouveauté de la version 2010.

Dans SharePoint Designer, on sélectionne la web part et dans l’onglet « Design » on clique sur « Customize XSLT -> Customize entire view »

image

La définition de la web part est alors modifiée et contient la transformation XSLT.

image

Celle-ci est très verbeuse car il s’agit de la transformation native qui permet un affichage dynamique en fonction des colonnes sélectionnées quel que soit la requête CAML sous-jacente. On y trouve aussi des appels à des fonctions javascript qui permettent la multi-sélection, le rafraîchissement automatique Ajax et autres fonctionnalités natives des listes SharePoint.

Nous allons maintenant externaliser cette transformation dans un fichier xslt séparé. Cela permet une édition plus pratique et apporte la possibilité versionner, approuver et surtout de réutiliser la transformation dans plusieurs XSLT List View Web Parts. Cette étape est optionnelle car il est possible de modifier la transformation directement dans la définition de la web part, mais je tenais à mettre en avant cette possibilité dans ce billet.

Copions tout le contenu de la balise <xsl></xsl> dans un fichier CustomAnnonces.xslt, et uploadons ce fichier dans la bibliothèque Style Library du site.

image

Maintenant que le fichier contenant la transformation XSLT est enregistré dans le site SharePoint, nous devons indiquer à la XSLT List View Web Part l’url du fichier xslt à utiliser. Pour cela, dans le navigateur, on modifie les propriétés de la web part et on spécifie l’url relative serveur du fichier dans le champ « Lien XSL » de la section « Divers ».

image

A ce stade, on peut travailler dans SharePoint Designer pour modifier la transformation CustomAnnonces.xslt. A chaque modification, on peut enregistrer le fichier et rafraichir son navigateur pour constater le changement.

Il n’est pas toujours nécessaire de conserver les centaines de lignes de la transformation XSLT par défaut. La transformation native dite « schema independant » s’appuie sur la requête CAML (passée en paramètre de la transformation) pour renvoyer un rendu HTML en fonction des colonnes sélectionnées dans l’affichage. Plus d’informations sur cette logique « schema independant » sont disponibles dans ce billet : SharePoint 2010 List View Blog Series: Part 3 – List View Architecture.

Pour l’exemple, je ne veux afficher que le champ titre et le champ body avec un rendu HTML très simple sous forme de balises ul / li, et je n’ai pas besoin de conserver les fonctionnalités natives que sont le filtrage dynamique, la multi sélection, etc.

On peut vider le contenu de la balise principale et redéfinir la transformation comme on le souhaite :

custom xslt

Après enregistrement, voici le résultat dans le navigateur :

rendu personnalisé

 

Au travers de cet exemple simpliste, nous avons vu comment avec SharePoint Designer :

- Personnaliser la requête CAML sous-jacente d’une XSLT List View Web Part

- Personnaliser la transformation XSLT assurant le rendu HTML

- Externaliser la transformation XSLT dans un fichier séparé

Avec une mise en forme CSS et un peu de JavaScript, les possibilités de cette web part peuvent devenir très intéressantes puisqu’elle permet de répondre à énormément de besoins métiers sans nécessiter le déploiement de code serveur. Cette web part va sans nul doute faire beaucoup parler d’elle dans les mois à venir.

 

Arnault Nouvel - Winwise

SharePoint 2010 : Configuration du courrier sortant et du courrier entrant dans une machine virtuelle

sharepoint-server

Utile pour certaines démonstrations, formations, ou pour des développements spécifiques, la configuration du courrier entrant et sortant dans une machine virtuelle SharePoint 2010 n’est plus aussi aisée qu’auparavant. En effet, le serveur mail de Windows Server 2003 n’a pas été porté dans les éditions 2008 et 2008 R2 de Windows Server, seul le serveur SMTP a été conservé.

Nous allons voir comment configurer le mail sortant et le mail entrant dans une machine virtuelle SharePoint 2010 disposant de l’environnement logiciel suivant :

  • Windows Server 2008 R2 (nom de machine : SPF2010)
  • Active Directory Domain Services (nom de domaine : office14.local)
  • SQL Server 2008 R2
  • SharePoint Foundation 2010
  • Office Professional 2010

Pour mettre en place le courrier entrant et le courrier sortant sur la même machine virtuelle, l’astuce va consister à utiliser conjointement :

  • un serveur mail complet (SMTP + IMAP) pour les boites mail de nos utilisateurs ainsi que pour le courrier sortant (hMailServer)
  • le serveur SMTP de Windows Server pour le courrier entrant

Nous allons tout d’abord installer un serveur mail gratuit téléchargeable sur internet : hMailServer. Celui-ci fera office de serveur mail pour nos utilisateurs ainsi que de serveur SMTP pour le courrier sortant.

 

1. Configuration de hMailServer

hMailServeur est disponible au téléchargement à l’adresse suivante : http://www.hmailserver.com/

Installer hMailServer en laissant toutes les options par défaut.

Une fois hMailServer installé, connectons nous à son interface d’administration intitulée hMailServer Administrator :

hMailServer-ajout-domaine1

Via le bouton « Add domain… », ajoutons le domaine office14.local correspondant au domaine de notre Active Directory.

hMailServer-ajout-domaine1

La seule information à spécifier est le nom du domaine.

Configurons maintenant le serveur mail pour y désactiver certains paramètres de sécurité qui pourraient nous gêner pour la suite :

Dans Settings -> Advanced -> Auto-ban : décocher la case “enabled” et enregistrer.

hMailServer-autoban

Dans Settings -> Advanced -> IP Ranges -> Internet, décocher les cases concernant l’authentification du serveur SMTP, puis enregistrer :

hMailServer-auth

Notre serveur de messagerie est maintenant opérationnel.

 

2. Configuration DNS

Nous allons configurer le DNS afin qu’Outlook puisse configurer automatiquement les adresses de messagerie correspondant au nom de domaine office14.local.

Avant tout, vérifions dans les propriétés de la carte réseau que son serveur DNS principal est le serveur DNS local

dns-principal

Créons ensuite une entrée MX dans le DNS Manager (Tous les programmes -> outils d’administration –> DNS).

dns-mx

On laissera le premier champ vide afin de créer une entrée MX par défaut, et on sélectionnera le host correspondant à notre machine virtuelle dans le 2ème champ :

dns-mx2

La configuration nécessaire est maintenant effectuée. Créons un compte de messagerie administrator@office14.local pour l’utilisateur OFFICE14\Administrator.

 

3. Création de boites de messagerie

hMailServer permet de créer automatiquement des boites de messagerie pour les utilisateurs enregistrés dans l’Active Directory.

Pour chaque utilisateur de l’Active Directory pour lequel on souhaite créer une boite mail, nous allons suivre la procédure suivante :

Click droit sur le noeud Accounts, puis “Add AD Account”.

hMailServer-ajout-utilisateur

On sélectionne les utilisateurs pour lesquels on souhaite créer une boite de messagerie.

hMailServer-ajout-utilisateur2

Pour chaque utilisateur pour lequel on crée boite de messagerie, on inscrira l’adresse de messagerie correspondante dans l’Active Directory :

AD-user-mail

On peut maintenant ouvrir Outlook 2010 pour configurer le compte administrator@office14.local.

outlook-configuration-utilisateur

Grâce à la configuration DNS mise en place précédemment, l’assistant va configurer automatiquement le compte de messagerie :

outlook-configuration-utilisateur2

On répètera cette procédure pour tous les comptes utilisateurs pour lesquels on souhaite disposer d’une boîte de messagerie dans Outlook 2010.

 

4. Configuration de SharePoint 2010 pour le courrier sortant

Le courrier sortant permet à SharePoint d’envoyer des emails aux utilisateurs (alertes sur les listes, quotas, etc.). Voyons comment le configurer.

Dans l’administration centrale de SharePoint, cliquer sur « System Settings » puis « Configure Outgoing E-Mail Settings »

Indiquer le nom de la machine virtuelle comme sur la capture, ainsi que l’adresse mail au nom de laquelle SharePoint enverra des mails :

sharepoint-courrier-sortant

Pour tester, le plus simple est de créer une alerte avec l’utilisateur Administrator sur n’importe quelle liste SharePoint, car lors de sa création un email de confirmation est envoyé instantanément.

On vérifiera tout d’abord que SharePoint connait l’email associée au compte utilisateur Administrator, dans ses paramètres personnels :

sharepoint-mail-utilisateur

Ensuite on crée une alerte sur une liste (la liste Resources dans l’administration centrale par exemple).

sharepoint-creer-alerte

On constate alors dans Outlook 2010 que SharePoint a envoyé un message à administrator@office14.local 

confirmation-alerte

 

5. Configuration du serveur SMTP pour le courrier entrant

Le courrier entrant est une fonctionnalité très intéressante de SharePoint puisqu’il permet de créer du contenu dans des listes et bibliothèques SharePoint à partir de mails envoyés par les utilisateurs. Pour le mettre en place, il faudra créer un sous-domaine (ici sharepoint.office14.local) réservé à cette usage et le router vers un serveur SMTP dédié.

Nous allons pour cela configurer le serveur SMTP de Windows Server. En effet, le SMTP de hMailServer utilise une structure de fichier qui ne pourrait pas convenir à SharePoint.

Pour installer le serveur SMTP de Windows Server, on utilise l’écran d’ajout de feature Windows :

smtp-feature

Une fois la feature installée, on peut ouvrir la console d’administration de IIS 6.0 pour y configurer l’instance SMTP.

Le port 25 étant déjà utilisé par hMailServer, nous allons devoir configurer le SMTP sur un port différent : ici, le port 6925.

Clic-droit sur le noeud SMTP Virtual Server –> Properties -> bouton Advanced –> Edit :

configuration-smtp

Ensuite, on modifie le nom de domaine par défaut pour qu’il corresponde au sous-domaine que l’on souhaite réserver pour les adresses mails des listes SharePoint. Ici, on utilisera sharepoint.office14.local :

configuration-smtp-domaine

On peut alors démarrer le serveur SMTP.

smtp-start

Une chose importante reste à faire dans hMailServer. Il faut router les emails dont les destinataires sont xxxxx@sharepoint.office14.local vers le serveur SMTP Windows, en précisant le port 6925, comme indiqué dans la capture :

hMailServer-routage

 

6. Configuration de SharePoint 2010 pour le courrier entrant

Dans l’administration centrale, cliquer sur « System Settings », puis « Configure incoming e-mail settings ». Puisque nous hébergeons localement le serveur SMTP, il suffit de sélectionner le mode automatique et de préciser le sous-domaine choisi pour les adresses mails :

sharepoint-courrier-entrant

Pour tester, associons une adresse mail à une bibliothèque de documents. Dans les paramètres de la bibliothèque de documents de l’administration centrale, cliquer sur « Incoming e-mail settings ».

On peut alors associer une adresse de messagerie à la bibliothèque :

sharepoint-courrier-entrant-bibliotheque

Pour tester, nous allons ouvrir Outlook et écrire un mail à notre bibliothèque de documents, avec en pièces jointes les documents que l’on souhaite y envoyer :

courrier-entrant-test

Par défaut, le job SharePoint qui va récupérer les messages dans le drop folder du serveur SMTP s’exécute toutes les minutes. Il faut donc patienter quelques secondes avant que le document apparaisse dans la bibliothèque :

courrier-entrant-test2

 

Conclusion

Dans ce billet, nous avons vu comment mettre en place le courrier entrant et le courrier sortant sur une seule machine virtuelle, en utilisant conjointement un serveur mail gratuit et le serveur SMTP de Windows Server. Nous avons aussi vu comment créer des boites de messageries pour les utilisateurs de notre Active Directory, et comment les configurer dans Outlook 2010.

Je n’ai trouvé aucun serveur mail gratuit permettant de gérer le courrier entrant, n’hésitez pas à laisser un commentaire si vous en connaissez un.

 

Arnault Nouvel - Winwise

SharePoint 2010 et IIS 7.0 : URL rewriting pour un site public internet

sharepoint-server

Les fanatiques du référencement le savent, la profondeur des url a un impact sur le référencement naturel. Dans SharePoint, les pages d’un site de publication étant situées dans une bibliothèque “Pages”, les url sont sous la forme http://www.monsite.com/sous-site/Pages/ma-page.aspx et ont donc un niveau d’arborescence inutile voir polluant d’un point de vue référencement.

Dans le cadre d’un projet de site internet hébergé sur SharePoint 2010, on m’a demandé de réfléchir à une solution d’url rewriting pour faire disparaitre /Pages/ de l’url.

Le challenge dans cette tâche était de la réaliser sans apporter de contrainte pour les auteurs du site et surtout sans impacter le fonctionnement de SharePoint.

C’est alors que j’ai découvert le plug-in Url Rewrite (actuellement en RC v2.0) pour IIS 7.0. Celui-ci a pour particularité de permettre l’url rewriting en entrée et en sortie via des règles équivalentes à des replace().

 

Afin de ne pas impacter le “back-office” de SharePoint, on peut étendre la web application (interne) et configurer Url Rewrite sur une 2ème web application (publique).

Les 2 web applications reposent alors sur la même base de données de contenu mais sont configurables indépendamment l’une de l’autre au niveau IIS :

  • http://edit.monsite.com (Zone Default) l’application utilisée par les auteurs, accessible depuis le réseau de l’entreprise. On ne modifie pas la configuration de cette web app. 
  • http://www.monsite.com (Zone Internet) l’application publique pour les internautes, sur laquelle on désactive l’authentification Windows. C’est sur celle-ci que l’on configure Url Rewriter.

Une fois Url Rewriter RC v2.0 installé, lorsqu’on sélectionne un site dans IIS, une nouvelle icone apparait permettant d’accéder à l’interface de paramétrage d’url rewriting.

IIS Url rewrite

 

L’interface de paramétrage permet de définir des règles pour le rewriting d’entrée et le rewriting de sortie basées sur des expressions régulières :

url rewrite

 

Inbound rules

La liste du haut permet de spécifier les règles de redirection selon l’url requêtée. Seule la partie relative au site est testée.

Par exemple pour  http://www.monsite.com/site1/site2/default.aspx, c’est la chaine “site1/site2/default.aspx” qui est testée avec nos expressions régulière. Lorsqu’un utilisateur requête l’application IIS, les règles sont testées (et appliquées en cas de succès) dans l’ordre affiché. En l’occurrence, nous pouvons transformer cette adresse en http://www.monsite.com/site1/site2/Pages/default.aspx, la véritable url de la page que sait interpréter SharePoint.

Dans la capture d’écran, la première règle correspond au cas de la chaine vide. Son rôle est que si l’internaute requête http://www.monsite.com, c’est la page http://www.monsite.com/Pages/default.aspx qui sera servie sans pour autant rediriger l’utilisateur via un code 302. C’est un cas particulier pour la page d’accueil.

La deuxième règle correspond au cas d’une page située dans une bibliothèque de pages (contient /Pages/). Dans ce cas l’url n’a pas besoin d’être réécrite puisqu’il s’agit du cas '”natif”, d’où le Action Type = None.

La troisième règle correspond au cas d’une page située dans /_layouts/. Là encore, on ne modifie pas l’url.

Enfin, la 4ème règle permet de convertir une url http://www.monsite.com/site1/default.aspx en http://www.monsite.com/site1/Pages/default.aspx. Les balises {R:1} et {R:2} correspondent aux groupes matchés par l’expression régulière, équivalent de $1 et $2 dans les langages classiques.

Grâce à ces 4 règles, on bénéficie d’un Url Rewriting satisfaisant permettant d’accéder aux pages de publication SharePoint sans préciser /Pages/ dans l’url, pour un référencement plus efficace.

Récapitulatif des règles d’entrée

Pattern Action Type Rewrite Stop Processing
^$ Rewrite Pages/default.aspx True
(.*/)?pages/.* None   True
(.*/)?_layouts/.* None   True
^(.*/)?([^\/]*?\.aspx)$ Rewrite {R:1}Pages/{R:2} True

 

Outbound rules

Maintenant la partie la plus fun, et la nouveauté de la RC 2.0 de Url Rewriter : le rewriting de sortie !

Les règles du 2ème bloc (de la capture plus haut) permettent de modifier le contenu HTML d’une page servie par l’application avant qu’elle ne soit renvoyée à l’utilisateur. Ce mécanisme permet de supprimer /Pages/ de toutes les url trouvées dans la réponse HTML.

Attention cependant, ce “filtre” s’applique sur tout ce qui est renvoyé par le site web. Cela inclut les images, les scripts, les fichiers… Si on ne limite pas le périmètre de notre transformation aux seuls fichiers HTML, on s’expose à des erreurs serveur.

On va donc configurer une pré-condition permettant de limiter le périmètre de notre transformation aux réponses http dont le content type  commence par “text/HTML”, comme ceci :

HTML pre-condition

Une fois notre pré-condition configurée, on peut configurer notre rewriting de sortie :

image

 

Cette règle se veut très simple afin de limiter l’impact sur les performances : le processus de transformation en sortie étant gourmand en ressources, plus notre expression régulière sera simple, plus l’impact sur les performances sera faible. Cette règle va donc transformer toutes les occurrences de /Pages/nom-de-page.aspx en /nom-de-page.aspx sans se préoccuper du fait qu’il s’agisse ou non d’une url. Attention donc aux cas particuliers :)

Dans mon cas, je ne souhaite pas seulement modifier les url des liens mais aussi les url sous forme de texte, pour modifier l’affichage des résultats de recherche par exemple. Pour limiter la transformation aux liens, j’aurais pu utiliser un filtre prévu à cet effet (“Match the content within:” sur la capture ci-dessus) afin de gagner en performances.

Une dernière chose importante, d’après mes tests il faut désactiver la compression dynamique sur le site IIS sous peine d’erreurs serveur.

Règle de sortie

Pattern Action Type Rewrite
/Pages/([^\s\.]*?\.aspx) Rewrite /{R:1}

 

Conclusion

L’impact sur les performances n’est pas négligeable (traitement supplémentaire + incompatibilité avec la compression dynamique), mais dans un cas où le référencement est stratégique cette solution s’avère efficace et relativement simple à mettre en place. Sa principale force est d’être 100% transparente pour l’application SharePoint.

Compatible avec SharePoint 2007 et SharePoint 2010,  le réel pré-requis est IIS 7.0.

Des tests de montée en charge étant prévus dans les semaines à venir, je ne manquerai pas de publier un retour d’expérience sur cette solution.

TechDays 2010 : Les slides de la session migration SharePoint 2010

sharepoint-server

Comme promis lors de la session “Quel plan de migration prévoir de SharePoint 2007 vers SharePoint 2010”, voici le lien pour télécharger les slides de notre présentation :

Techdays 2010 - Migration SharePoint 2010.pptx

Besoin d’assistance pour préparer la migration vos fermes vers SharePoint 2010 ? N’hésitez pas à nous contacter Antoine Dongois et moi.


Classé sous ,

TechDays 2010 : Migration de MOSS 2007 à SharePoint Server 2010

sharepoint-server

Mardi 9 février à 11H lors des TechDays 2010, avec mon collègue Antoine Dongois, j'aurai le plaisir de co-animer la session "Quel plan de migration prévoir de SharePoint 2007 à SharePoint 2010"

Nous y décrirons les différents chemins de migration possibles avec leurs avantages et leurs inconvénients, et ferons une démo de migration de type DB Attach de bout en bout.

Rendez-vous aux TechDays!

SharePoint 2010 : Utiliser le modèle objet dans un exécutable

sharepoint-server

 

Lorsqu’on essaie pour la première fois d’utiliser le modèle objet de SharePoint 2010 dans une application console ou autre exécutable .NET classique, l’aventure peut commencer par l’erreur FileNotFoundException suivante :

error2

 

Ce problème est normal : par défaut, Visual Studio configure la plupart des projets pour une plateforme x86. Mais pour utiliser le modèle objet de SharePoint 2010, un projet devra être configuré pour une plateforme x64.

Cela se configure en quelques clics. Voici comment faire :

 

solution-explorer

Dans le Solution Explorer, un clic-droit sur le nœud de la solution, puis Configuration Manager.

 

configuration-manager

On crée ensuite une nouvelle “Platform” via le bouton New…

 

configuration-manager2

La fenêtre propose alors de créer une plateforme “Any CPU” (compatible x64), on peut valider sans rien changer.

 

configuration-manager3

Le projet est maintenant correctement configuré, relançons l’application pour tester.

 

success

L’objet SPSite s’instancie maintenant correctement.

 

Ce problème n’est pas seulement lié à la classe SPSite. En pratique, la première fois que j’ai eu ce problème, c’était avec un appel à SPFarm.Local qui renvoyait null si le projet est configuré en x86.

Selon le type d’objet que l’on cherche à manipuler, on aura donc soit une référence nulle, soit une erreur lors de l’instanciation.

Dans les deux cas, rien n’indique au développeur que le problème est lié à la configuration de plateforme du projet. Il devra donc en avoir conscience pour exploiter le modèle objet dans un projet “non-SharePoint”.

Heureusement, les nouveautés de SharePoint 2010 ainsi que les nouvelles possibilités offertes par Visual Studio 2010 pour le développeur SharePoint feront vite oublier ce petit désagrément :)

 

Arnault Nouvel

SharePoint 2007 : jQuery.SPItemsRotator pour afficher des éléments de listes avec animation

 

Avoir des sites “qui bougent” est une demande récurrente des utilisateurs qui s’occupent de la mise en place de sites SharePoint. Il est vrai qu’à l’heure du web 2.0, on s’attend à voir de belles animations dans nos navigateurs, même dans un intranet.

Si on souhaite avoir des éléments de liste qui apparaissent les uns après les autres avec un fondu, on peut procéder de différentes manières :

  • Avec une web part avec code serveur + javascript, ce qui implique le déploiement d’un package sur le serveur
  • Avec une DataView web part + javascript, ce qui n’est pas toujours simple à mettre en place, surtout pour un utilisateur final
  • Avec du javascript uniquement, avec accès aux données par web service. Ce n’est pas optimal d’un point de vue performances, mais permet une intégration rapide si la solution est générique et clé-en-main. 
  • Je passe volontairement sur l’option Silverlight qui est encore plus contraignante dans SharePoint 2007

Le plug-in jQuery.SPItemsRotator correspond à la 3ème option. Il s’intègre en quelques minutes et permet d’afficher les éléments d’une liste d’annonce standard ou d’une bibliothèque d’images standard, avec des fondus entre chaque élément ou image. Ceci ne requiert pas le déploiement de package sur la ferme, tout se fait avec SharePoint designer. Attention cependant, pour les sites publics internet il est vaut mieux choisir les options 1 ou 2 pour des raisons de performances et de sécurité.

jQuery.SPItemsRotator s’appuie sur jQuery et jQuery.SPServices, un plug-in jQuery qui propose une API pour les web services SharePoint. Il est très utile et bien pensé, je ne peux que vous encourager à y jeter un œil.

Après avoir référencé les 3 fichiers JavaScript dans la master page du site à l’aide de SharePoint Designer, on peut placer des jParts (le terme à la mode) pour obtenir quelque-chose comme ceci :

Les jParts sont en pratique des Content Editor Web Parts qui contiennent du code HTML et jQuery :

<!-- Web Part de gauche -->
<div id="divAnnouncements" style="height:150px;"></style>
<script>
   $(window).ready(function(){                                
           $("#divAnnouncements").rotateAnnouncements({
            webURL:"/sites/spitemsrotator", //url du site
            listName:"My Announcements" //nom de la liste
      });
   });
</script>
<!-- Web Part de droite -->
<div id="divPictures" style="height:200px;"></style>
<script>
   $(window).ready(function(){                                
         $("#divPictures").rotatePictures({
            webURL:"/sites/spitemsrotator",
            listName:"My Pictures",
            fadeDuration:1500, //durée du fading en millisecondes
            displayDuration:5000 //durée d'affichage de chaque élément en millisecondes
      });
   });
</script>

Voilà pour l’utilisation basique des méthodes rotatePictures et rotateAnnouncements.

Celles-ci définisse une requête CAML et un formatage précis puis appellent rotateItems, la méthode principale. Un développeur ou un utilisateur avancé peut aussi appeler cette méthode pour afficher et filtrer les éléments en spécifiant la requête CAML, et les formater comme il veut en décidant du pattern de rendu.

Pour exemple prenons le code de rotateAnnouncements, qui peut servir de source d’inspiration pour écrire d’autres rotateXXXXX() méthodes :

jQuery.fn.rotateAnnouncements = function(options){

    var opts = $.extend({
        displayDuration:10000,
        fadeDuration:1500,
        CAMLViewFields:"<ViewFields><FieldRef Name='Body' /></ViewFields>",
        CAMLQuery:"<Query><Where><Or><IsNull><FieldRef Name='Expires' /></IsNull><Geq><FieldRef Name='Expires' /><Value Type='DateTime'><Today /></Value></Geq></Or></Where></Query>",    
        itemHtml:"{ows_Body}"
    }, options);

    return this.rotateItems(opts);
    
}

Un dernier avantage, il est aussi facile de placer la zone de rotation dans une page que dans la master page : au lieu d’utiliser une Content Editor Web Part, on peut placer le HTML+script directement dans la master page avec SharePoint Designer.

Bonnes rotations :)

SharePoint 2007 : Nouvelle version de WSPCompare

Je me suis récemment surpris à utiliser WSPCompare uniquement pour regarder le contenu d’un package .wsp. Suite à ce constat, j’ai publié une nouvelle version de WSPCompare qui dispose d’une interface plus appropriée à ce type d’utilisation.

L’outil propose donc maintenant 2 modes d’utilisation.

Mode navigation si 1 package est ouvert

Browse

Mode comparaison si 2 packages sont ouverts

Compare

 

Je me suis aussi amusé à réaliser une petite vidéo de présentation de l’outil :

 

Pour télécharger WSPCompare, c’est par ici : http://wspcompare.codeplex.com


Classé sous , , ,

SharePoint 2007 : WSPCompare

Il arrive parfois d’avoir besoin de comparer 2 versions d’un package .wsp pour SharePoint, par exemple après le refactoring d’une solution, ou lors de la mise en place d’un build TFS.

L’objectif est en général de vérifier qu’aucun fichier n’a été oublié, et que le manifest.xml est cohérent.

J’ai donc développé WSPCompare, un outil permettant de comparer le contenu de 2 solutions SharePoint. Celui-ci est comme d’habitude publié sur CodePlex.

Après avoir sélectionné les 2 fichiers .wsp à comparer, l’outil présente la liste de tous les fichiers trouvés, en utilisant un code couleur pour identifier les différences :

WSPCompareSummary

L’onglet Détails permet de visualiser le contenu des fichiers pour pouvoir les comparer :

WSPCompareDetails

Pour les téléchargements, c’est par ici : http://wspcompare.codeplex.com/Release/ProjectReleases.aspx


Classé sous , , ,

SharePoint 2007 : Consommer un web service avec jQuery

 

Background

Je suis récemment intervenu sur un projet SharePoint dans une très grande entreprise. Pour faire court, dans cette entreprise l'équipe intranet en charge de la ferme SharePoint fournit une collection de sites aux équipes métier qui en font la demande. De par l'architecture de SharePoint, l'équipe métier est alors autonome pour gérer sa collection de sites.

Une de ces équipes m'a sollicité pour les aider à la mise en place de leur projet de portail. La collection de sites comprend un sous-site visible par tout les utilisateurs, et un sous-site par pays (chaque utilisateur a accès au site de son pays, + quelques autres pour les responsables par continent) dont l’administration serait déléguée à une équipe dans chaque pays. Mon client m'a donc demandé de développer une page d'accueil dynamique qui proposerait des liens pour chacun des sous-sites accessibles par l'utilisateur courant, avec une mise en forme classique : titre, logo et la description du sous-site.

Sans hésitation, j’explique qu’il suffit de développer une web part dédiée qui utiliserait le modèle objet de SharePoint pour identifier les sous-sites auxquels l’utilisateur a accès, et qu’il y en aurait au grand maximum pour une journée. En effet, dans le cadre de mon intervention de 5 jours, le temps de développement se devait d’être court.

Mon client valide l’idée et je contacte l’équipe intranet pour vérifier que le déploiement d’un package .wsp ne poserait pas de problème. Manque de chance, c’était sans compter sur la longueur de la procédure de validation, en particulier durant cette période des vacances. Nous sommes vite arrivés à la conclusion qu’il était risqué de miser sur un déploiement en production en moins de 5 jours : 

  • Rédaction d’un cahier des charges “officiel”
  • Réunion avec l’équipe intranet pour discuter du cahier des charges et de l’impact du développement sur la ferme
  • Développement et packaging .wsp de la web part en question
  • Revue de code par l’équipe intranet
  • Déploiement en environnement de recette
  • Tests + validation par l’équipe intranet
  • Tests + validation par l’équipe métier (avec la possibilité d’un retour à la case départ)
  • Déploiement en production (nocturne)

Tout cela est 100% justifié : il faut garder à l’esprit qu’un bug ou une faille dans un développement SharePoint, aussi insignifiant soit-il, peut mettre en péril la stabilité de la ferme SharePoint, et l’intégrité des données de la majeure partie de l’intranet. De plus cette procédure devrait être répétée à chaque fois qu’une modification du composant serait nécessaire dans le futur. Le jeu en valait-il vraiment la chandelle ?

Non, surtout qu’en appelant les web services SharePoint par JavaScript, on peut obtenir un résultat équivalent. C’est une approche moins classique, mais qui permet de gagner du temps sur le packaging, le déploiement et la maintenance du composant. L’appel au web service sera grandement facilité par l’utilisation de jQuery, le framework JavaScript à la mode qui dispose d’une méthode dédiée facile d’utilisation, et surtout cross-browser.

 

Implémentation

Avant d’aller plus loin il fallait valider qu’un web service pouvait me fournir les informations dont j’avais besoin. Le SDK de SharePoint 2007 détaille chaque web service disponible : http://msdn.microsoft.com/en-us/library/ms445760.aspx

Webs.asmx dispose d’une méthode GetWebCollection qui retourne la liste des sous-sites du site courant avec leurs titres et url, et GetWeb.asmx retourne le titre, le code langue et la description d’un site donné grâce à l’url passé en paramètre. Bingo !

J’ai référencé dans la master page du site les fichiers JavaScript (celui de jQuery et le mien) placés dans la bibliothèque “Styles Library”, et inséré sur la page d’accueil une web part de contenu éditable contenant :

  • une balise (qui accueillera le menu)
  • une ligne de JavaScript pour exécuter ma méthode de remplissage de cette balise.

CEWP

Pour l’édition du fichier .js, on peut se servir de SharePoint Designer pour naviguer facilement jusqu’à l’emplacement de notre fichier javascript et de le modifier directement, tout en bénéficiant d’une reconnaissance syntaxique du JavaScript.

Ensuite, sur une machine virtuelle MOSS 2007, on peut récupérer l’enveloppe soap correspondante à la méthode que l’on souhaite appeler à l’aide de son navigateur. En l’occurrence, j’ai saisi l’url “http://localhost/_vti_bin/Webs.asmx” dans mon navigateur, et cliqué sur le lien de la méthode GetWebCollection :

Definition

On peut alors écrire la partie JavaScript. La méthode FillMenu() appellera le web service en utilisant l’enveloppe SOAP que l’on vient d’obtenir et remplira le menu. Voici le contenu du fichier JavaScript :

   1:  
   2: var wsWebsUrl;
   3:  
   4: function FillMenu(){
   5:     
   6:     wsWebsUrl = GetWebServiceUrl("Webs.asmx");
   7:     
   8:     if(wsWebsUrl != null){
   9:         
  10:         var request = " \
  11:        \
  12:          \
  13:        \
  14:     ";
  15:       
  16:         $.ajax({
  17:             url: wsWebsUrl,
  18:             type: "POST",
  19:             dataType: "xml",
  20:             data: request,
  21:             success: FillMenu_GetWebCollectionHandler,
  22:             contentType: "text/xml; charset=\"utf-8\"",
  23:             async:true
  24:         });
  25:         
  26:     }
  27: }
  28:  
  29: function FillMenu_GetWebCollectionHandler(response){
  30:     
  31:     var menuRows = "";
  32:  
  33:       // Pour chaque sous élément Web
  34:       $(response).find("Web").each(function() {
  35:          
  36:          var webTitle = $(this).attr("Title");
  37:          
  38:          if(webTitle != "Group"){ //On ne souhaite pas proposer de lien vers Group
  39:              
  40:              var webUrl = $(this).attr("Url");
  41:              
  42:              //récupération des informations relatives au site cible
  43:              var webInfos = FillMenu_GetWebInfos(webUrl)    
  44:              if(webInfos != null){
  45:  
  46:                 var webDescription = $(webInfos).attr("Description");
  47:                 var webLogo = webUrl + "/images/logo.png"; //les logos sont stockée dans chaque site
  48:         
  49:                 // On ajoute une entrée au menu
  50:                 menuRows += " \
  51:                                  \
  52:                                     \
  53:                                          \
  54:                                      \
  55:                                  \
  56:                                  \
  57:                                     " + webTitle + " \
  58:                                     
" + webDescription + "
\
  59:                                  \
  60:                             ";
  61:             }
  62:         }
  63:     });
  64:     
  65:     //on cache, on remplit, et on fait apparaitre le menu avec une animation
  66:     $("#testWS").hide().append(""
+ menuRows + "").slideDown();
  67:  
  68: }
  69:  
  70: function FillMenu_GetWebInfos(webUrl){
  71:     
  72:     var requestWebs = " \
  73:        \
  74:          \
  75:        \
  76:     ";
  77:  
  78:     var returnValue = null;
  79:     
  80:     //appel synchrone qui ne réussit que si l'utilisateur a le droit d'accès au site cible
  81:     $.ajax({
  82:         url: webUrl + "/_vti_bin/Webs.asmx",
  83:         type: "POST",
  84:         dataType: "xml",
  85:         data: requestWebs,      
  86:         contentType: "text/xml; charset=\"utf-8\"",
  87:         async:false,
  88:         success:function(response){
  89:  
  90:                     //construction de la requete soap avec insertion de l'url du site en paramètre
  91:                     var requestWeb = " \
  92:                                        \
  93:                                          \
  94:                                           " + webUrl + " \
  95:                                          \
  96:                                        \
  97:                                     ";
  98:             
  99:                     //appel synchrone pour récupérer les informations du site cible
 100:                     $.ajax({
 101:                         url: wsWebsUrl,
 102:                         type: "POST",
 103:                         dataType: "xml",
 104:                         data: requestWeb,      
 105:                         contentType: "text/xml; charset=\"utf-8\"",
 106:                         success:function(response){
 107:  
 108:                                     returnValue = $(response).find("Web");
 109:                                 },
 110:                         async:false
 111:                     });
 112:  
 113:                 }
 114:         
 115:     });
 116:     
 117:     return returnValue;
 118:  
 119: }
 120:  
 121: // Récupération de l'url du web service dans le contexte du site courant
 122: function GetWebServiceUrl(filename){
 123:         
 124:     //    inspiré par SharePoint Bookmarklets : http://imtech.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=19047
 125:     var UrlPart={Url:0,ServerRelativeUrl:1,Site:2,SiteCollection:3};
 126:     var urlRegex=/^https?:\/\/[^\/]+(((\/(?:sites\/[^\/]+\/)?).*?)((lists\/([^\/]+)|pages|_catalogs|_layouts|_wpresources|_vti_bin|([^\/]+)\/forms|[^\/]+\.aspx).*)?)$/i;
 127:     var url=window.location.href.toLowerCase();
 128:     var match=urlRegex.exec(url);
 129:     
 130:     if( match == null)
 131:         return null
 132:     else
 133:         return match[UrlPart.Site] + "_vti_bin/" + filename;
 134:         
 135: }

On notera l’utilisation d’un appel asynchrone au web service pour la récupération des sous-sites et d’un appel synchrone pour la récupération de la description d’un site. Cet appel synchrone était préférable dans mon cas pour pouvoir remplir ma grille au fur et à mesure.

Voici le résultat :

Capture

Edit : Un appel à GetWebCollection sur chaque sous-site est nécessaire pour déterminer si l’utilisateur courant y a accès.Le security-trimming n’est pas aussi efficace que je le pensais. Le code a été modifié en fonction.

Conclusion

Au final, j’ai gagné un temps fou. Certes, le menu apparait quelques dixièmes de secondes après que la page soit chargée, mais j’ai pu effectuer ce développement sans déployer de code sur la ferme. La maintenance est facilitée, et les risques d’impacts négatifs sur les serveurs sont nuls : Au pire des cas, l’utilisateur aura une erreur JavaScript.

Les web services SharePoint permettent d’obtenir la plupart des informations dont on a besoin lors d’un développement classique, de manipuler les listes, leurs éléments (insertion, modification, suppression), et bien plus encore. Il y a même un web service pour faire de la correction orthographique!

Après avoir vu la vidéo sneak peak de SharePoint 2010 pour les développeurs, en particulier la démo sur l’intégration d’applications Silverlight, on devine que les web services qui composent le “modèle objet client” seront enrichis et améliorés (WCF). On constate que la stratégie de Microsoft est de limiter l’impact d’un développement sur une ferme SharePoint, et de donner plus d’autonomie aux développeurs en réduisant les interactions nécessaires avec les administrateurs de ferme. Les développeurs pourront déployer eux-même leurs applications Silverlight sans risque. En effet, si un développement ne comporte pas de code serveur et qu’il n’interagit avec les données que via des web services qui prennent en compte la sécurité, il n’y a aucun risque pour la santé de la ferme et l’intégrité de ses données. SharePoint pourra alors devenir la plateforme de choix pour toutes les applications intranet, et le coeur du système d’information de l’entreprise.

Il est déjà possible dans SharePoint 2007 d’héberger des applications Silverlight, mais cela nécessite l’intervention d’un administrateur de ferme pour effectuer les modifications nécessaires sur la ferme.

JavaScript est à mon sens l’équivalent dans SharePoint 2007 de ce que sera Silverlight dans SharePoint 2010 en terme de facilité d’intégration. Alors autant s’y mettre tout de suite car les développements 100% client utilisant les web services pour manipuler les données, c’est l’avenir !


Classé sous , ,

MOSS 2007 : jQuery picture viewer Web Part

Récemment, un client m’a demandé de lui développer une web part qui afficherait une bibliothèque d’images sous forme de miniatures, permettant de les télécharger, et de les visualiser en taille réelle via un click de souris.

Ces spécifications m’ont rappelé une superbe démonstration d’Aurélien Verla sur jQuery lors de sa présentation Découverte des nouveautés d'ASP.NET 4.0 aux TechDays 2009.

J’ai donc souhaité intégrer cela dans MOSS 2007, en utilisant la puissance de la Content by Query Web Part (CQWP) afin de bénéficier de fonctionnalités de tri et de filtrage sur les images affichées. Le tout est bien sûr réalisable entièrement dans SharePoint Designer, mais l’envie m’a pris d’aller un peu plus loin que les spécifications initiales et, pourquoi pas, publier ce projet sur CodePlex.

Lien : MOSS 2007 jQuery picture viewer Web Part

Capture

 

Pour avoir la possibilité de télécharger les images, au lieu de les ouvrir simplement dans Internet Explorer, la web part réutilise la page /_layouts/download.aspx, qui existe nativement dans SharePoint. Elle permet de télécharger un fichier dont l’url est passé dans la query string.

Exemple : http://office12-server/sites/t1/_layouts/download.aspx?sourceUrl=http://office12-server/sites/t1/Picture%20Library/Tree.jpg

Cette url renvoie le fichier Tree.jpg de la bibliothèque d’image “Picture Library” située dans la collection de sites http://office12-server/Sites/t1

 

Comme dit plus haut, il est possible de faire la même chose sans déployer de package sur la ferme. Ce développement est ainsi packagé afin de rendre la taille des miniatures paramétrable, de laisser la possibilité de désactiver les animations jQuery, et de pouvoir utiliser 2 fois la web part sur une même page avec un paramétrage différent.

Un développeur MOSS pourrait donc télécharger les sources et utiliser les fichiers .xsl, .js et .css pour intégrer la web part avec SharePoint Designer. Cela ne nécessiterait ni le déploiement de package sur le serveur, ni l’accord de l’administrateur de la ferme :)
A peu de choses près, il suffirait de modifier le fichier JavaScript afin qu’il s’initialise automatiquement, sans appel généré par du code serveur.

SharePoint 2007 : WSPDownload

Après BlobCacheAdmin, je vous propose aujourd’hui une nouvelle page d’administration pour l’administration centrale de SharePoint.

Inspirée par le post de Kype, cette page permet aux administrateurs de télécharger les solutions packages .wsp présents dans le solution store d’une ferme SharePoint.

Après installation de WSPDownload.wsp, un nouveau lien apparaitra dans l’onglet Operations de l’administration centrale :

WSPDownloadLink WSPDownloadForm 

Vous pourrez alors cliquer sur un des solution packages pour le télécharger.

Cet outil est disponible au téléchargement sur CodePlex dans le projet SharePoint Administrator Packages : http://spadminpack.codeplex.com

SharePoint 2007 : BlobCacheAdmin - Page d’administration du Blob Cache

Le Blob Cache permet d’utiliser du cache disque dans les sites SharePoint, ce qui évite des aller-retours avec la base de données pour la récupération de fichiers images, videos, ou autres. Il repose sur une ligne de configuration qu’il fallait jusqu’ici modifier à la main dans le fichier Web.Config de chaque serveur frontal, avec le risque d’erreur que cela induit.

J’ai développé une page d’administration pour permettre à un administrateur de définir ce paramétrage directement dans la Central Administration de SharePoint. Celle-ci utilise la classe SPWebConfigModification, afin que tous les serveurs frontaux soient mis à jour automatiquement. Comme l’explique Sébastien dans son post Gestion des Applications Web SharePoint et sites IIS : Best Practices, cela permet aussi que de nouveaux serveurs ajoutés à la ferme soient mis à jour automatiquement.

Disponible sur CodePlex, BlobCacheAdmin.wsp met à disposition une nouvelle page dans la Central Administration :

Lien dans la page Application Management
BlobCacheAdminLink

Page d’administration du Blob Cache
BlobCacheAdmin

Ce package est le premier de mon nouveau projet CodePlex, SharePoint Administrator Packages, qui réunira plusieurs outils d’administrations.

SharePoint : Optimiser le temps de développement et packager avec WSPBuilder

Ce post a pour but de présenter la manière dont je travaille au quotidien sur mes projets SharePoint, qui s'appuie sur l'outil WSPBuilder (la version en ligne de commande).
Il fait suite à un projet de démonstration que j'ai réalisé avec Visual Studio 2005 dans le cadre d'une formation sur le développement SharePoint. Il contient quelques features et peut servir de modèle lors du démarrage d'un projet SharePoint. Je le propose donc au téléchargement :
DemoWSP.zip

Un projet SharePoint qui se respecte doit  être livré sous la forme d'un SharePoint Solution Package, afin de pouvoir être déployé sur une ferme SharePoint par son administrateur.
Si on attend la fin du développement avant de s'intéresser au packaging, on risque d'aboutir à un refactoring couteux en temps, une solution en forme de plat de spaghettis, et une facture d'aspirines salée.
Alors que si on pense dès le début du projet à la problématique du packaging, la solution sera plus propre, et on gagnera en temps de développement et de maintenance. C’est là qu’intervient la magie de WSPBuilder, outil open source permettant d'automatiser le packaging d'un développement SharePoint.

L'avantage principal de WSPBuilder par rapport aux extensions Visual Studio pour WSS (qui de manière transparente génèrent un package et le déploient à chaque fois qu’on presse F5) est que le développeur exerce un réel contrôle sur la structure et le contenu de son package. De plus, ce modèle permet le déploiement de fichiers dans des répertoires non supportés par les extensions visual studio pour WSS: 12\Resources, 12\TEMPLATE\LAYOUTS, 12\TEMPLATE\IMAGES, etc.

Bien que WSPBuilder soit indépendant de Visual Studio (dans sa version en ligne de commande), la manière la plus pratique de l'utiliser est de créer un projet vide dans Visual Studio (qu'on appellera projet de packaging) dans lequel on créé 3 répertoires :
- un répertoire "12" qui aura la même structure de sous répertoire que celle du répertoire 12 de SharePoint,
- un répertoire "GAC" dans lequel iront les assemblii à déployer dans le GAC,
- et un répertoire "80" dans lequel iront les fichiers à déployer dans les répertoires des web applications.

Chacun de ces répertoires est optionnel, mais dans la majorité des cas on aura besoin des répertoires "12" et "GAC". Il suffit alors d'exécuter WSPBuilder.exe dans le répertoire du projet de packaging pour générer le package .wsp.
Note : Il est conseillé de renommer le package en .cab et de vérifier son contenu, car il peut arriver que WSPBuilder ne produise pas le résultat attendu. Il faut alors utiliser la méthode traditionnelle : création manuelle du .ddf et manifest.xml. Cela m'est arrivé une seule fois, c'est donc très rare, d’ailleurs je pense que le problème a été corrigé dans une des dernières versions de l’outil.

De par la simplicité d'utilisation de WSPBuilder, il n'est pas rare de trouver en entreprise des projets qui l'exécutent directement dans le build-event du projet.
Cela a deux avantages :
- après chaque compilation, un package à jour est généré et il suffit de le déployer via STSADM pour le tester.
- Si on travaille avec Team System, Il suffit dans le team build d'indiquer la solution cible pour que le package .wsp soit généré à chaque compilation, sans paramétrage supplémentaire. Ca, c’est génial.

Malheureusement, en phase de développement il y a de sérieux désavantages :
- Chaque compilation déclenche une génération du package ce qui prend beaucoup plus de temps
- Il faut ensuite déployer le package via STSADM ce qui prend aussi pas mal de temps

Je vais présenter ici la manière dont je travaille, qui permet d'optimiser le temps d'attente entre le moment de la compilation du projet et le moment où on en apprécie le résultat dans son navigateur web, tout en bénéficiant des avantages de WSPBuilder.
L'idée est de faire un déploiement "manuel" en phase de développement pour minimiser le temps d'attente, et de n'utiliser WSPBuilder que lors d'une compilation en mode Release (pour les team builds, et de temps en temps pendant la phase de développement pour ne pas avoir à modifier le fichier web.config de la web application à la main).
Ce modèle requiert un temps de préparation pour organiser la solution et créer les scripts au début du projet, mais le gain de productivité pour la suite est appréciable. Les temps d'attentes entre les déploiements et les tests seront réduit au strict minimum.
 

Solution


La solution Visual Studio comprendra 3 projets de type class library :

- DemoWSP : Le projet de packaging qui servira de structure de répertoires conforme à ce qu'attend WSPBuilder. Il contient les fichiers à déployer dans répertoire 12 (features, resources, etc.), un répertoire GAC et un répertoire 80.
- DemoGAC qui produit une assembly signée (contenant le code applicatif), vouée à être déployée dans le GAC.
- Demo80 qui produit une assembly non signée (contenant le code des web parts), vouée à être déployée dans le répertoire bin de la web application. 

Afin de s'assurer que notre projet pourra être compilé sur un serveur TFS, on prévoit un répertoire Lib dans le répertoire de notre solution, on y place les assemblii SharePoint, et on les référence dans nos projets à partir de ce répertoire Lib.
On devra aussi installer WSPBuilder sur le serveur TFS dans le même répertoire que sur nos machines virtuelles de développement (ou le mettre lui aussi dans le répertoire Lib).

A gauche, une capture d’écran de la structure de ma solution.

 

Le projet de packaging dispose d'un build-event qui appelle CreatePackage.bat lors d'une compilation en mode Release :

BuildEvents

En phase de développement, j'utilise deux scripts situés à la racine du projet de packaging:
- QuickDeploy.bat qui effectue un déploiement complet, copiant chaque fichier au bon endroit et recyclant ma pool d'application. Il met environ 1 seconde à s'exécuter, énorme gain comparé à la méthode consistant à générer puis déployer le package.
- HiveDeploy.bat qui copie uniquement les fichiers du répertoire 12, ne nécessitant pas le recyclage de ma pool d'application. L’exécution étant instantanée, ce script est très utile lorsque l'on effectue une modification sur une page .aspx par exemple, on peut la tester immédiatement car l’application n’a pas besoin d'être redémarrée.

HiveDeploy.bat
Ici, on tire le meilleur parti de WSPBuilder car on profite du fait que la structure de répertoires attendue correspond exactement à celle du répertoire 12. Il suffit de copier notre répertoire 12 dans celui de SharePoint.

@echo off
@SET SPDIR="c:\program files\common files\microsoft shared\web server extensions\12"

cls

ECHO.
Echo ================= Copying files to 12 directory ====================
ECHO.
xcopy /e /y /q 12\* %SPDIR%
ECHO.

QuickDeploy.bat
De même,  on copie notre 12 dans le 12 de SharePoint, ensuite on installe chaque assembly dans son répertoire cible. Enfin, on recycle le pool d'application que l'on utilise pour nos tests.

@echo off
@SET SPDIR="c:\program files\common files\microsoft shared\web server extensions\12"
@SET GACUTIL="C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe"

cls

ECHO.
Echo ================= Copying files to 12 directory ====================
ECHO.
xcopy /e /y /q 12\* %SPDIR%

ECHO.
Echo =================== Installing assemblies to GAC ===================
ECHO.
%GACUTIL% /uf "DemoGAC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53439e9aa159f9fe" /silent
%GACUTIL% -if ..\DemoGAC\bin\debug\DemoGAC.dll
ECHO.

echo ========== Installing assemblies in Web Application's bin ==========
ECHO.
xcopy /e /y /q ..\Demo80\bin\debug\Demo80.dll "C:\Inetpub\wwwroot\wss\VirtualDirectories\80\bin\"
ECHO.

Echo ================== Recycling Application Pools =====================
ECHO.
cscript C:\WINDOWS\System32\iisapp.vbs /a "SharePoint - 80" /r
ECHO.

Ce qui donne a l’écran :

QuickDeploy


CreatePackage.bat
On récupère les assemblii provenant de nos 2 autres projets, puis on exécute WSPBuilder afin que le package .wsp soit généré.

@echo off
@SET WSPBUILDER="M:\Utilitaires\WSPBuilder\WSPBuilder.exe"
cls

ECHO ================== Updating solution directory =====================
ECHO.
REM deleting bin directory to make sure WSPBuilder won't include unnecessary assemblies in the package
rd .\bin /s /q
copy ..\Demo80\bin\release\Demo80.dll .\80\bin\
copy ..\DemoGAC\bin\release\DemoGAC.dll .\GAC\

ECHO.
ECHO ================== Building package with WSPBuilder ===================
%WSPBUILDER% -WSPName DemoWSPBuilder.wsp -DLLReferencePath ..\Lib\
ECHO.


Pour s’assurer que les assemblii soient compilés avant l’appel à CreatePackage.bat, il faut aussi faire attention à ce que le projet de packaging soit "compilé" en dernier en paramétrant le build-order de la solution. Pour ce faire, on déclare une dépendance de DemoWSP sur les 2 autres projets :

BuildOrder1   Dependencies

 

Remarques :
- WSPBuilder génère automatiquement toutes les policies et balises SafeControl nécessaires, un autre avantage de l’outil. Lorsque c’est nécessaire, pendant la phase de développement, on peut donc déployer le package généré pour éviter d’avoir à faire ces modifications à la main dans le fichier web.config.
- Ce modèle de travail peut être étendu aux solutions utilisant le pattern MVP, moyennant quelques modifications des scripts .bat.

SharePoint 2007 : Comment utiliser la page d’attente standard

Parfois lors d’un gros traitement, un retour serveur peut durer plusieurs secondes. Il est alors d’usage d’informer l’utilisateur qu’un traitement est en cours, comme le fait SharePoint pour toutes les opérations longues, telles que la création d’un site par exemple.

L’objet de ce post, c’est que la page utilisée par SharePoint est prévue pour être utilisée dans vos développements WSS ! Son utilisation est d’ailleurs particulièrement simple: on manipulera juste un objet de type SPLongOperation que l’on instancie en passant une instance de la page courante.

Code

Les propriétés LeadingHTML et TrailingHTML permettent de spécifier le texte affiché dans la page d’attente.

Sur l’appel de la méthode Begin(), l’utilisateur sera transféré sur celle-ci jusqu’à l’appel de la méthode End(). Attention donc à appeler End() quoi qu’il arrive sinon l’utilisateur restera coincé sur la page d’attente. End() prend en paramètre l’url vers laquelle l’utilisateur doit être redirigé.

image

Comme d’habitude avec les objets SharePoint, les bonnes pratiques imposent qu'on libère les ressources via la méthode Dispose(), ou en utilisant l’objet dans un bloc using.

SharePoint 2007 : Zones et URL internes

Les URL des liens internes dans SharePoint doivent dépendre la zone du site. Lorsqu' on récupère une URL par code, il faut donc faire faire attention à la zone de l' instance d' SPSite utilisée.

Toutes les URL obtenues via le modèle objet de SharePoint dépendent de cette zone, y compris les valeurs des champs de type URL tels que les liens internes ou les photos des utilisateurs.

image 

Lorsqu' on ne précise pas la zone dans le constructeur de SPSite, la zone par défaut (définie dans l' administration centrale) est utilisée. Sur la capture d'écran, on remarque que l' Extranet est configuré comme zone par défaut.

Pour récupérer des URL dans un contexte Web, on privilégiera donc l' utilisation de l' instance SPContext.Current, qui expose des objets correspondant toujours à la zone de la requête en cours.

Si la récupération de l' URL requiert l' utilisation de SPSecurity.RunWithElevatedPrivileges, on est amené à devoir instancier un SPSite différent de celui exposé par SPContext.Current. Il faut alors utiliser le constructeur disposant du paramètre SPUrlZone pour afficher une URL correcte.

 image

Un tel oubli peut être particulièrement gênant lorsqu' il s' agit d' URL d' images. D' autant que lorsqu' on développe, on n' a pas forcément le réflexe de tester nos pages en zone Extranet.


Classé sous , , ,

SharePoint : Détecter un changement de valeur de propriété Personalizable

Dans le cadre du développement d'une évolution (gestion du VB.NET) pour la CodeTesterWebPart, je me suis rendu compte que le modèle objet de la WebPart ne permet pas de savoir directement, sur le set d'une propriété personalizable, si ce set correspond à une action utilisateur (changement de valeur) ou à l'initialisation du contrôle (affectation de la valeur sauvegardée).

N'ayant pas trouvé de documentation sur le sujet, j'ai remonté les deux callstack :

  • Dans le cadre de l'initialisation du contrôle, la propriété est affectée par le SPWebPartManager sur l'événement PageInitComplete de la page.
  • Dans le cadre d'un changement de valeur de propriété par l'utilisateur, le set de la propriété est appellé depuis le RaisePostBackEvent de la page (après le load donc). D'ailleurs, si nous sommes dans ce cas, il s'agît toujours d'une requête POST.

Dans une requête POST, la méthode CreateChildControls() de la WebPart est appellé juste avant le Load de la page, c'est à dire après que la propriété soit initialisée, mais avant qu'elle puisse être changée.

Partant de cette observation, j'utilise la propriété ChildControlsCreated pour déterminer s'il s'agit d'une initialisation ou d'un changement de valeur :

Language


Classé sous , ,

Sharepoint : La CodeTesterWebPart en pratique

Lorsque l'on développe sous Sharepoint, à chaque fois que l'on modifie son code il faut compiler, packager, déployer, recycler son application pool, et attendre que Sharepoint s'initialise. On peut alors vérifier l'éxécution du code si on a la chance d'avoir un bouton qui le déclenche directement, mais celui-ci peut aussi être éxécuté en fin de workflow ou lors d'évènements plus ou moins évidents à reproduire.

Tester du code dans son contexte d'exécution est donc souvent une tâche longue et fastidieuse pour le développeur Sharepoint.

La CodeTesterWebPart permet la compilation et l'éxécution instantanée de code C#. Elle est disponible depuis quelques jours sur CodePlex : http://www.codeplex.com/CodeTesterWebPart

On dispose d'une boite de texte dans laquelle on saisit le code que l'on souhaite, et d'un bouton "Run that method!" qui permet de l'éxécuter instantanément. On peut modifier la liste des assemblies référencées (en spécifiant leur chemin sur le disque) ainsi que les usings :

ListLists

Le bouton "Save Inputs" permet de sauvegarder les références, usings, et le code grâce au concept de Personnalization.

Si il y a des erreurs de compilation, elles sont affichées :

CompilerResults

La méthode de test dispose de deux arguments permettant de tester du code d'interface graphique : la page courante et un placeholder qui est situé juste en dessous du corps de la webpart. Ici, on joue avec un diagramme de gantt et on injecte du javascript dans la page :

Gantt2

Autre avantage, le code s'exécute avec le contexte courant. On peut donc tester tout ce qui concerne les droits. Par exemple, avec un compte visiteur, si on tente de modifier le titre du site courant avec la CodeTesterWebPart, on est redirigé vers la page Access Denied. On se rend alors compte qu'il faut utiliser  SPSecurity.RunWithElevatedPrivileges, le code suivant fonctionne sans problème :

SPSecurity

On pourrait se dire que les limites sont atteintes dès lors que l'on veut tester du code récursif. Non, c'est possible aussi ! Il suffit de tricher un peu sur le parenthèsage. Après tout, le code est compilé tel qu'il est rendu à l'écran.

Recursive

Mais comment ca marche ?!

Le namespace System.CodeDom contient des classes qui permettent de compiler dynamiquement une assembly à partir de code source sous forme de chaine de caractères. La CodeTesterWebPart compile ainsi une assembly en mémoire, il n'y a pas d'écriture sur le disque. La méthode TestMethod est ensuite appellée par reflection, de manière tout à fait classique. Pour les plus curieux, le code source est disponible sur CodePlex.

Sharepoint : Développez plus vite avec la CodeTesterWebPart

Et si on pouvait vérifier que notre code s'exécute comme prévu avant même de le déployer?

Voici la CodeTesterWebPart :

CodeTesterWebPart

Téléchargement : http://www.codeplex.com/CodeTesterWebPart

Conçue dans ce but, j'espère que cette Web part vous fera gagner du temps lors de vos prochains développements :)

Sharepoint : Le contrôle ScriptLink

Le contrôle ScriptLink de Sharepoint permet d'effectuer l'enregistrement d'un fichier javascript dans une page. Voici les avantages de ce contrôle par rapport au ClientScript.RegisterClientScriptInclude() d'ASP.NET :

  • Gestion de la localisation
  • Gestion du cache
  • Le script est toujours référencé dans le header

Les deux choses importantes à savoir avant de l'utiliser sont que le chemin à spécifier doit être relatif au répertoire LAYOUTS (si utilisation sans localisation), et que si le fichier n'est pas trouvé une erreur sera levée.

Dans le cadre d'une utilisation avec localisation, Sharepoint cherchera la version qui correspond à la langue du site dans LAYOUTS/XXXX, où XXXX est le LCID du site courant (1033 pour l'anglais, 1036 pour le français).

Voyons un exemple d'utilisation avec une web part. Chacun des fichiers .js contient une méthode HelloWorld qui lance une alerte avec un message different. Voici la structure et le code du projet :

SLDemo

Dans ce cas c'est le fichier LAYOUTS/SLDemo/ScriptLinkDemo.js qui est référencé :

Neutral

Maintenant essayons avec nos versions localisées, en changeant juste la valeur de Localizable :

Localizable

Résultat à l'éxécution avec un site en francais (LCID 1036):

French

C'est la version qui correpspond au LCID du site qui est utilisée, on peut le vérifier en examinant le code HTML de la page:

Script2

Mais que se passe-t-il si il n'y a pas de fichier pour ce LCID ? Essayons. J'efface le fichier SLDemo/1036/ScriptLinkDemo.js, un coup de iisreset.exe, et je recharge ma page :

Error

On remarque que ni la version neutre ni la version anglaise n'ont été substituées par Sharepoint, et que le message d'erreur n'indique pas le chemin exact attendu. Attention donc à deployer une version par langue susceptible d'être utilisée.


Classé sous , ,
Plus de Messages Page suivante »

Les 10 derniers blogs postés

- Utilisation de la réplication SQL dans le code .NET d'une application mobile - Implémentation & Conseils : PARTIE 1/3 (classe SqlCeReplication & premi... par Le Blog de Pi-R (Pierre Cambier) le il y a 1 heure et 7 minutes

- Quelques trucs intéressants (05/09/2010) par CoqBlog le il y a 18 heures et 50 minutes

- EnumerableCollection par Matthieu MEZIL le 09-03-2010, 23:23

- [HS] Chrome 6 + Moi = Coup de gueule ! par Blog de Jérémy Jeanson le 09-03-2010, 12:12

- [WP7] Utiliser un WrapPanel dans une application Windows Phone 7 par Blog Technique d'Audrey PETIT le 09-01-2010, 22:40

- [WP7] Besoin d’avoir des données en cache par Nicolas Humann le 09-01-2010, 15:12

- [TFS] Comment forcer la saisie d’un Area ou Iteration par Atteint de JavaScriptite Aiguë [Cyril Durand] le 09-01-2010, 02:01

- SQL : Fonctions d'agrégation MIN/MAX et valeurs NULL par CoqBlog le 09-01-2010, 00:04

- Votez pour Warnygo par Nicolas Humann le 08-31-2010, 11:58

- [SharePoint 2010] L'administration toolkit V1 est disponible par Julien Chable le 08-31-2010, 01:42