Me voilà de retour pour vous présenter une autre nouveauté de WSS 3.0 : le ListFieldIterator. C’est quoi donc ? C’est un control WSS chargé de lister les colonnes à afficher pour la plupart des listes de WSS que ce soit en mode consultation, modification ou création. Pour connaitre la liste de ces colonnes, ce dernier s’appuie sur la définition de la liste.
Je vais donc, tout au long de cet article, vous expliquer comment fonctionne les iterators et comment créer le votre. Comme à mon habitude, je vais dérouler un exemple complet de mise en œuvre afin de vous aider à mieux comprendre l’intérêt de ce control. L’exemple et le suivant : Je souhaite créer mon propre modèle de liste représentant un catalogue de produit. Cette liste, basée sur le modèle de liste personnalisée, aura comme particularité de ne pas afficher, en consultation, le fournisseur ainsi que le prix d’achat du produit sauf si l’utilisateur a les droits de création et/ou modification sur cette liste. La copie d’écran ci-dessous présente le formulaire générer par WSS lors de la création d’un nouvel élément :
Pour réussir cela, je vais créer mon propre ListFieldIterator : ce dernier se chargera de spécifier quels sont les champs qui ne doivent pas être affichés et dans quelle conditions. Ensuite, je créerai un rendu spécifique pour mon modèle de liste qui utilisera mon ListFieldIterator. Pour finir, je modifierai la définition de ma liste pour que cette dernière utilise le rendu spécifique lors du mode « consultation ». Vous n’avez pas tout compris ? Ce n’est pas bien grave. Je vais détailler toutes ces étapes. Attention : vous allez rentrer dans le monde hardcore de la customisation WSS 3.0.
Une dernière remarque avant de commencer :
J’utilise une version US de WSS 3.0 sur un serveur Windows 2003 également US. Les répertoires peuvent varier des répertoires en FR.
Un ListFieldIterator est un control WSS 3.0 surchargeable qui est chargé de définir, pour la plupart des listes, quel champ doit être affiché et quel champ ne doit pas l’être. Pour ce faire, il défini une méthode IsFieldExcluded. Cette méthode retourne False si le champ doit être affiché, True dans le cas contraire. Ce control est disponible dans l’assembly Microsoft.SharePoint.dll. Pour créer notre propre ListFieldIterator, nous allons donc créer notre propre classe qui va hériter de la classe ListFieldIterator et surcharger la méthode IsFieldExcluded.
Commençons par créer un nouveau projet Visual Studio 2005 de type ClassLibrary. Comme pour tous les développements spécifiques SharePoint, nous devons signer notre assembly pour qu’elle puisse être utilisée par SharePoint. Ensuite, nous allons rajouter à notre projet les références aux assmblies suivantes :
- Microsoft.SharePoint
- System.Web
Créons ensuite notre ListFieldIterator sous la forme d’une classe héritant de Microsoft.SharePoint.WebControls et surchargeons la méthode IsFieldExcluded. Voici le code que nous obtenons :
using System;
using System.Collections.Generic;
using System.Text;
namespace Bewise.Wss3.Sample.ListFieldIterator
{
public class DisplayCatalogueIterator
: Microsoft.SharePoint.WebControls.ListFieldIterator
{
protected override bool IsFieldExcluded(Microsoft.SharePoint.SPField field)
{
return base.IsFieldExcluded(field);
}
}
}
Que fait donc cette classe et comment marche-t-elle ? Lorsque WSS 3.0 doit générer le rendu d’une liste (que ce soit en consultation, modification ou création), il utilise cette classe pour déterminer, pour chacun des champs qui compose l’élément à afficher, si le champ doit être affiché ou non.
Pour mon exemple, nous souhaitons créer un iterator qui n’affichera pas les colonnes « Fournisseur » et « Prix d’achat» si l’utilisateur n’a pas les droits de création et/ou modification sur la liste. Nous allons donc surcharger la méthode IsFieldExcluded de telle façon qu’elle retourne True pour les colonnes précédente dans le cas qui nous intéresse. Dans les autres cas, nous laisserons le traitement par défaut. Voici le code que nous obtenons :
using System;
using System.Collections.Generic;
using System.Text;
namespace Bewise.Wss3.Sample.ListFieldIterator
{
public class DisplayCatalogueIterator
: Microsoft.SharePoint.WebControls.ListFieldIterator
{
protected override bool IsFieldExcluded(Microsoft.SharePoint.SPField field)
{
if ((!this.List.DoesUserHavePermissions(SPBasePermissions.AddListItems))
&& (!this.List.DoesUserHavePermissions(SPBasePermissions.EditListItems)))
{
if (field.Title == "Fournisseur")
return true;
else if (field.Title == "Prix d'achat")
return true;
else
return base.IsFieldExcluded(field);
} else
return base.IsFieldExcluded(field);
}
}
}
Que fait maintenant notre iterator ? Chaque fois qu’il est appelé pour savoir s’il doit afficher ou non un champ, on regarde si l’utilisateur courant a les droits de création et/ou modification sur la liste. Si tel n’est pas le cas, on va regarder via l’objet field passé en paramètre si le champs courant fait parti des champs que nous ne voulons pas afficher. Si c’est le cas, on retourne True pour signaler que ce champ doit être exclu. Dans tous les autres cas, on laisse le traitement par défaut ;
Remarque :
J’utilise la propriété Title de l’objet SPField pour déterminer quel champ est entrain d’être évalué. Cette propriété retourne le nom visible de la colonne courante.
Ca y est ! Notre iterator est terminé. Il ne nous reste plus qu’à le compiler et à le déployer dans le GAC, sur notre serveur WSS 3.0. Mais, ça n’est pas encore fini ! Il va maintenant falloir créer une liste capable d’utiliser notre Iterator.
Pour comprendre comment mettre en place notre ListFieldIterator, il faut commencer par comprendre comment WSS 3.0 réalise le rendu des listes. Alors, comment fait-il ?
Chaque liste est décrite sous la forme d’un fichier XML : C’est se que l’on appel une « List Definition ». Ce fichier XML d’écrit l’ensemble des colonnes qui composent la liste, leur nom interne, leur nom visible, leur format, … Ce fichier XML spécifie également le nom du template de rendu que WSS 3.0 doit utiliser pour générer la page .aspx pour consulter un élément de la liste, le modifier ou en créer un nouveau.
C’est ce dernier point en particulier qui nous intéresse. Nous verrons plus loin, après avoir générer le schéma xml qui décrit notre liste où ce fait exactement ce lien.
Mais c’est quoi un template de rendu ?
Ce sont des templates de codes ASP.NET 2.0 décrivant les contrôles qui vont êtres utilisés par WSS pour afficher une partie d’une page. Ces modèles sont disponibles dans le répertoire « TEMPLATE\CONTROLTEMPLATES » du répertoire d’installation de WSS 3.0. La plupart des listes WSS s’appuie sur le template nommé ListForm. Ce template est déclaré dans le fichier « defaultTemplates.ascx » comme suit :
<SharePoint:RenderingTemplate ID="ListForm" runat="server">
<Template>
<SPAN id='part1'>
<SharePoint:InformationBar runat="server"/>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop"
RightButtonSeparator=" " runat="server">
<Template_RightButtons>
<SharePoint:NextPageButton runat="server"/>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
<SharePoint:FormToolBar runat="server"/>
<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0
cellspacing=0 width=100%>
<SharePoint:ChangeContentType runat="server"/>
<SharePoint:FolderFormFields runat="server"/>
<SharePoint:ListFieldIterator runat="server"/>
<SharePoint:ApprovalStatus runat="server"/>
<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>
</TABLE>
<table cellpadding=0 cellspacing=0 width=100%>
<tr>
<td class="ms-formline">
<IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt="">
</td>
</tr>
</table>
<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px">
<tr>
<td width=100%>
<SharePoint:ItemHiddenVersion runat="server"/>
<SharePoint:ParentInformationField runat="server"/>
<SharePoint:InitContentType runat="server"/>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl"
RightButtonSeparator=" " runat="server">
<Template_Buttons>
<SharePoint:CreatedModifiedInfo runat="server"/>
</Template_Buttons>
<Template_RightButtons>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
</td>
</tr>
</TABLE>
</SPAN>
<SharePoint:AttachmentUpload runat="server"/>
</Template>
</SharePoint:RenderingTemplate>
C’est donc ce template qui défini comment un élément sera affiché, les boutons qui seront disponibles et les contrôles ASP.NET et WSS qui seront utilisées pour générer le rendu. Une ligne m’intéresse plus particulièrement dans cet extrait de code :
<SharePoint:ListFieldIterator runat="server"/>
Cette ligne définit le ListFieldIterator utilisé par défaut pour déterminer quels sont les champs à afficher ou à masquer. Donc, maintenant, si vous avez compris comment WSS fonctionne pour générer le rendu d’une page, vous avez compris ce que nous allons devoir faire pour mettre en place notre propre rendu utilisant notre iterator pour notre modèle de liste.
Il va nous falloir :
- Créer notre propre définition de liste,
- Définir notre propre rendu qui référencera notre ListFieldIterator,
- Modifier la définition de notre liste afin de spécifier quel template de rendu WSS doit utiliser lors de la modification d’un élément de la liste.
OK, nous avons vu la théorie sur la génération de rendu. Alors, au boulot. Nous allons donc maintenant mettre tout ça en place sur notre propre définition de liste.
L’objectif ici est donc de créer notre fichier XML qui défini le schéma de notre liste. Nous pourrons ainsi le modifier pour qu’il utilise un template de rendu spécifique. Commençons donc par créer à la racine de notre collection de site notre liste que je nomme « Catalogue » avec les colonnes présentées dans la copie d’écran de l’introduction. Maintenant, il nous faut obtenir le schéma xml qui décrit cette liste.
Nativement, WSS ne fournit pas de solution pour exporter le schéma d’une liste. Par contre, il existe un outil nommé « SharePoint Solution Generator » qui fournit cette fonctionnalité. Cet outil est installé avec les extensions WSS pour Visual Studio 2005 (téléchargeable depuis http://www.microsoft.com/downloads/details.aspx?FamilyID=19f21e5e-b715-4f0c-b959-8c6dcbdc1057&Displa yLang=en).
Nous allons voir ensemble comment exporter le schéma depuis WSS avec cet outil. Lançons SPSolGen.exe :
Ce premier écran nous permet de sélectionner le type d’export que nous souhaitons réaliser. Dans notre cas, nous souhaitons générer la définition de la liste « Catalogue ». Nous allons donc sélectionner « List Definition » puis cliquer sur « Next »
L’écran suivant nous permet de choisir le site de la collection qui contient la liste à exporter. Nous allons donc sélectionner le site racine puisque c’est dans ce dernier que nous avons créé notre liste. Pour continuer l’assistant, cliquons ensuite sur « Next »
Cet écran présente toutes les listes disponibles sur le site racine. Nous allons cocher la liste que nous souhaitons exporter, à savoir, « Catalogue » puis cliquer sur « Next »
L’assistant nous demande ensuite de spécifier le répertoire d’export ou seront générer les fichiers.
Avant de réaliser l’export, l’assistant nous résume les tâches qu’il va exécuter. Cliquons sur « Start » pour démarrer l’export. Voilà ! Une fois terminé, SPSolGen à créer un répertoire dans lequel il à générer une solution Visual Studio 2005 contenant toutes les données de notre liste, à savoir :
- Le fichier schema.xml contenant la définition de notre liste
- 4 fichiers .aspx correspondants aux formulaires spécifiques d’affichage de notre liste
Dans le cas qui nous intéresse, nous allons modifier le rendu de nos formulaires sans toucher aux pages .aspx natives. Nous n’aurons donc pas besoins de ces 4 fichiers .aspx mais uniquement du schéma de notre liste.
Pour l’instant, nous allons laisser de côté note définition de liste. Nous reviendrons dessus un peu plus loin pour venir y spécifier le rendu à utiliser.
Bon, nous avons notre définition de liste. Il nous faut maintenant créer le template de rendu qui utilisera notre ListFieldIterator. Comment faire ça ? Rien de plus simple : nous allons partir du rendu par défaut (celui présenté dans le chapitre 3.2), en créer une copie et l’adapter pour nos besoins. Pour ce faire :
- Editons le fichier defaultTemplates.ascx disponible dans le répertoire \template\controlTemplates\ du répertoire d’installation de WSS 3.0.
- Copions toute la section qui correspond à la définition du template « ListForm »
- Collons cette copie tout en fin du fichier .ascx pour ne pas risquer d’écraser d’autres données.
- Renommons « ListForm » en « DisplayCatalogueForm »
Voilà, nous avons donc maintenant notre propre template de rendu. Il nous faut encore le modifier de t’elle manière qu’il utilise notre ListFieldIterator. Pour ce faire, modifions la ligne :
<SharePoint:ListFieldIterator runat="server"/>
Par
<Bewise:DisplayCatalogueIterator runat="server"/>
Il nous faut enfin déclare le préfix Bewise comme pointant vers notre assembly. Pour cela ajoutons en entête du fichier la déclaration suivante :
<%@Register TagPrefix="Bewise" Assembly="Bewise.Wss3.Sample.ListFieldIterator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=995401eda3d8bd0c" namespace="Bewise.Wss3.Sample.ListFieldIterator"%>
Une fois cela fait, voilà tout ce que nous avons ajouté au ficier DefaultTemplates.ascx :
<%@Register TagPrefix="Bewise" Assembly="Bewise.Wss3.Sample.ListFieldIterator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=995401eda3d8bd0c" namespace="Bewise.Wss3.Sample.ListFieldIterator"%>
…
<SharePoint:RenderingTemplate ID="DisplayCatalogueForm" runat="server">
<Template>
<SPAN id='part1'>
<SharePoint:InformationBar runat="server"/>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop"
RightButtonSeparator=" " runat="server">
<Template_RightButtons>
<SharePoint:NextPageButton runat="server"/>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
<SharePoint:FormToolBar runat="server"/>
<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0
cellspacing=0 width=100%>
<SharePoint:ChangeContentType runat="server"/>
<SharePoint:FolderFormFields runat="server"/>
<Bewise:DisplayCatalogueIterator runat="server"/>
<SharePoint:ApprovalStatus runat="server"/>
<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>
</TABLE>
<table cellpadding=0 cellspacing=0 width=100%>
<tr>
<td class="ms-formline">
<IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt="">
</td>
</tr>
</table>
<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px">
<tr>
<td width=100%>
<SharePoint:ItemHiddenVersion runat="server"/>
<SharePoint:ParentInformationField runat="server"/>
<SharePoint:InitContentType runat="server"/>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl"
RightButtonSeparator=" " runat="server">
<Template_Buttons>
<SharePoint:CreatedModifiedInfo runat="server"/>
</Template_Buttons>
<Template_RightButtons>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
</td>
</tr>
</TABLE>
</SPAN>
<SharePoint:AttachmentUpload runat="server"/>
</Template>
</SharePoint:RenderingTemplate>
Voilà, notre template de rendu est également prêt à fonctionner. Que nous reste-t-il à faire pour finir ?
Il nous faut donc maintenant faire le lien entre notre définition de liste et notre template de rendu. Pour faire cela, nous allons donc reprendre notre fichier schema.xml et le modifier pour lui spécifier d’utiliser notre template de rendu. Mais où est fait le lien vers ce template de rendu ?
Si nous fouillons un peu dans le fichier xml, nous trouvons la section suivante :
<ContentType ID="0x0100FAB92A4888E14E4C84B164BF30CD4B9A" Name="Item" Group="List Content Types" Description="Create a new list item." Version="4" FeatureId="{695b6570-a48b-4a8e-8ea5-26ea7fc1d162}">
<FieldRefs>
<FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" … />
…
<FieldRef ID="{6e69e83e-1842-4d25-8e4d-74bc1e720385}" … />
</FieldRefs>
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/...>
<FormTemplates xmlns="http://schemas.microsoft.com/...">
<Display>ListForm</Display>
<Edit>ListForm</Edit>
<New>ListForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
<Folder TargetName="Item" />
</ContentType>
Nous pouvons voir dans la section FormTemplates que pour chaque mode d’affichage d’un élément (item) de la liste (consultation, modification et création), le schéma xml spécifie le template de rendu ListForm (template de rendu utilisé par défaut). Par conséquent, si nous souhaitons que, pour le mode consultation, cette liste utilise le rendu de template que nous avons créé, nous devons modifier la ligne suivante :
<Display>ListForm</Display>
Par
<Display>DisplayCatalogueForm</Display>
Il reste encore une dernière modification à apporter à notre définition de liste avant qu’elle ne soit terminée. Lors de la génération du schéma xml, nous avons pu remarquer que l’outil SharePoint Solution Generator avait également générer les formulaires permettant de créer, consulter, modifier et lister les éléments de notre liste. En générant les formulaires spécifique, l’outil à également modifié le schéma xml pour que la définition de notre liste référence ces pages .aspx.
Comme nous n’avons apporté aucune modification à ces pages, pourquoi les déployer ! Nous allons donc modifier notre schéma xml pour faire en sorte que notre liste utilise les pages WSS par défaut. Les pages utilisées par la plupart des listes sont :
- form.aspx pour la création, la modification et la consultation
- viewpage.aspx pour l’affichage des vues
Ces pages sont stockées dans le répertoire template\pages du répertoire d’installation de WSS 3.0. Reprenons notre fichier xml et regardons la section Forms de plus prêt :
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main" />
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main" />
</Forms>
Cette section déclare, pour chaque mode, la page .aspx physique à utiliser. Pour référencer les pages précédemment citées, il nous faut rajouter à chaque élément Form l’attribut SetupPath avec comme valeur pages\form.aspx
En faisant cela, WSS utilisera la page spécifié par l’attribut SetupPath. La valeur spécifiée par l’attribut Url servira pour faire de l’url rewriting et masquer ainsi à l’utilisateur l’url réel de la page en cours. Voici ce que nous obtenons.
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx"
SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="EditForm" Url="EditForm.aspx"
SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="NewForm.aspx"
SetupPath="pages\form.aspx" WebPartZoneID="Main" />
</Forms>
La génération du schéma à également modifié la référence à la page permettant d’afficher la liste des éléments pour cette liste. Il faut donc, là aussi, restaurer le lien vers la page par défaut en remettant en place l’attribut SetupPath. Pour ce faire, nous allons modifier la ligne
<View DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" Type="HTML" DisplayName="All contacts" Url="AllItems.aspx" Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/images/contacts.png" WebPartZoneID="Main">
Par
<View DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" Type="HTML" DisplayName="All contacts" Url="AllItems.aspx" SetupPath="pages\viewpage.aspx" Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/images/contacts.png" WebPartZoneID="Main">
Et voilà, fini pour la déclaration de la liste …
Nous avons donc mis en tous les éléments nécessaires pour créer notre propre liste qui utilisera un ListFieldIterator particulier pour gérer le rendu d’un élément de notre liste de manière spécifique. Il ne nous reste plus qu’a déployer tout ça.
Pour utiliser les best-practice de SharePoint, nous allons déployer notre définition de liste sous la forme d’une feature.
Créons une arborescence t’elle que présentée ci-dessous, le fichier schema.xml correspond au schéma que nous avons modifié précédemment :
Contenu du fichier « Feature.xml »
<?xml version="1.0" encoding="utf-8"?>
<Feature Id=" 68C342A5-863A-4307-A905-8EDC29355442"
Title="Catalogue"
Description="Modèle de catalgue de produits"
Version="1.0.0.0"
Scope="Web"
Hidden="TRUE"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="ListTemplates\Element.xml" />
</ElementManifests>
</Feature>
Contenu du fichier « Element.xml »
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ListTemplate
Name="Catalogue"
Type="300"
BaseType="0"
OnQuickLaunch="TRUE"
FolderCreation="FALSE"
SecurityBits="12"
Sequence="1000"
DisplayName="Modèle de catalogue"
Description="Modèle de catalgue de produits"/>
</Elements>
Copions le répertoire « CatalogueList » dans le répertoire « template\features » de WSS 3.0. Ouvrons une fenêtre de commande et plaçons-nous dans le répertoire « bin » de WSS. Exécutons la première commande :
stsadm -o installfeature -filename cataloguelist\feature.xml -force
Cette première commande permet d’installer une feature. La suivante va nous permettre de l’activer.
stsadm -o activatefeature -filename cataloguelist\feature.xml –force –url http://xxx:yy
Pour finir, exécuter la commande IISRESET pour redémarrer l’application pool du site WSS.
Une fois déployé, nous apercevons un nouveau modèle de liste “Modèle de catalogue” (cf. écran 1). Créons une instance de cette liste pour vérifier qu’elle utilise bien notre template de rendu. A première vu, rien à changé. Maintenant, ajoutons un article dans notre liste. Puis consultons l’élément en tant qu’administrateur (cf. écran 2). Nous voyons bien tous les champs qui composent la liste. Reconnectons nous maintenant au site avec un profil « Reader » et retournons voir notre article (cf. écran 3) … YES ! le formulaire n’affiche pas le fournisseur ni le prix d’achat.
Ecran 1
Écran 2
Écran 3
Pour résumer, les ListFieldIterator permettent de modifier le rendu par défaut des formulaires en fonction d’un contexte, d’une valeur de la liste ou de toutes autres choses. A vous de laisser courir votre imagination …