5. Ajouter un menu

5.1. L'idée: un tableau

L'ajout d'un menu consiste visuellement à rajouter une boîte quelque part, contenant ce menu. Je connais trois manières d'ajouter un menu:

  • un tableau, avec une case pour le menu et l'autre case pour le reste de la page;

  • deux frames;

  • une boîte flottante grâce aux feuilles de styles CSS.

La seconde méthode n'a aucun interet car justement l'utilisation des transformations XSLT permet de se passer des frames qui sont plutôt une antiquité lourde. La troisième méthode nécessite que vous connaissiez en plus les feuilles de style assez bien pour comprendre les exemples. Je préfère me rabattre sur la méthode des tableaux car je ne dois pas me tromper de beaucoup en pensant que tout le monde qui lit cet article sait faire un tableau rudimentaire en HTML.

La page web résultante sera de cette forme:

[les en-têtes ici]
<body>
  <table border='0' width='100%'>
    <tr>
      <td>[ici, par exemple, le menu]</td>
      <td>[ici, par exemple, le texte]</td>
    </tr>
  </table>
</body>
</html>

Libre à vous de mettre le menu à gauche comme ici, ou à droite, ou même en haut en ajoutant une ligne au tableau.

5.2. Modifions la feuille de style XSLT

Ils s'agit de rajouter les données décrivant le menu quelque part pour que le processeur les prenne en compte lors de la génération de la page web. Le problème est que les processeurs XSLT prennent en entrée un seul document XML, et la seule autre entrée possible est d'utiliser des paramètres. Les paramètres ne sont pas suffisants pour cela, et nous voulons éviter d'avoir à spécifier le menu dans chaque page xhtml. Une solution est de mettre tout ce qui concerne le menu dans cette feuille de style. Mais mieux que cela, nous allons demander à la feuille de style de lire un fichier xml contenant la description du menu dans les différentes langues. Nous allons donc écrire ce fichier purement XML que je vous donne intégralement afin que vous ayez quelques traductions de base. Appelez ce fichier menu.xml par exemple.

5.3. menu.xml

 1 <?xml version="1.0" encoding="ISO-8859-1"?>
 2 <menu>
 3       <item name='index'>
 4               <text lang='en'>Home</text>
 5               <text lang='fr'>Accueil</text>
 6               <text lang='de'>Startseite</text>
 7               <text lang='es'>Inicio</text>
 8       </item>
 9 
10       <item name='news'>
11               <text lang='en'>News</text>
12               <text lang='fr'>Infos</text>
13               <text lang='de'>Neuigkeiten</text>
14               <text lang='es'>Noticias</text>
15       </item>
16 
17       <item name='features'>
18               <text lang='en'>Features</text>
19               <text lang='fr'>Fonctionnalités</text>
20               <text lang='de'>Features</text>
21               <text lang='es'>Características</text>
22       </item>
23 
24       <item name='screenshots'>
25               <text lang='en'>Screenshots</text>
26               <text lang='fr'>Captures d'écran</text>
27               <text lang='de'>Screenshots</text>
28               <text lang='es'>Screenshots</text>
29       </item>
30           
31       <item name='changes'>
32               <text lang='en'>Changes</text>
33               <text lang='fr'>Changements</text>
34               <text lang='de'>Änderungen</text>
35               <text lang='es'>Cambios</text>
36       </item>
37 
38       <item name='download'>
39               <text lang='en'>Download</text>
40               <text lang='fr'>Téléchargement</text>
41               <text lang='de'>Herunterladen</text>
42               <text lang='es'>Descarga</text>
43       </item>
44 
45       <item name='license'>
46               <text lang='en'>License</text>
47               <text lang='fr'>Licence</text>
48               <text lang='de'>Lizenz</text>
49               <text lang='es'>Licencia</text>
50       </item>
51 
52       <item name='credits'>
53               <text lang='en'>Credits</text>
54               <text lang='fr'>Remerciements</text>
55               <text lang='de'>Credits</text>
56               <text lang='es'>Créditos</text>
57       </item>
58 </menu>
			

Reprenons maintenant la feuille de style XSLT que nous allons modifier en plusieurs étapes.

5.4. Ajout d'un modèle

L'ajout d'un modèle se fait très simplement, avec quelque part, avant le dernier modèle, ceci:

1   <xsl:template name="menu" mode="menu">
2     <p class='menu'>
3       Menu
4     </p>
5   </xsl:template>

Si le processeur XSLT trouve la balise <menu>, il risque d'appliquer ce modèle. Cependant, il n'y a pas de balise <menu> dans notre document source, et en mettre un n'est pas le but. Ceci serait vrai s'il n'y avait pas l'attribut mode. Cet attribut fait que ce modèle ne peut être appliqué que dans certaines conditions. En effet, je vais vous montrer comment forcer l'utilisation d'un modèle, c'est-à-dire comment influer sur le déroulement du flux xml en entrée.

Pour forcer l'application d'un modèle, on utilise la balise <xsl:call-template name="menu" mode="menu"/>. C'est ici que vous voyez l'utilisation de l'attribut mode. Sans cet attribut, le modèle menu n'est pas appliqué puisque ce modèle a été défini avec mode="menu". Cet attribut permet entre autres d'appliquer plusieurs modèles différents pour un même élement, en fonction du mode choisi.

Voici ce que devient le modèle /body (version définitive):

 1   <xsl:template match="/body">
 2     <html>
 3       <xsl:comment>
 4 
 5         This web site is (c) 2003 Me
 6 	Feel free to get inspiration from this site, as soon as
 7 	you leave this comment here.
 8   
 9 
10         THIS PAGE WAS AUTOMATICALLY GENERATED FROM TEMPLATES.
11 	DO NOT MODIFY ANYTHING IN THIS FILE: IT WILL BE OVERWRITTEN
12 	AND ANY CHANGES WILL BE LOST.
13 
14       </xsl:comment>
15       <head>
16         <link rel="stylesheet" type="text/css" href="monprog.css" />
17         <title><xsl:apply-templates select="/body/h1[position()=1]/*"/></title>
18       </head>
19       <body>
20         <table class='main_table'>
21           <tr>
22             <td>
23               <xsl:call-template name="menu" mode="menu"/>
24             </td>
25             <td>
26               <xsl:apply-templates select="@*|*"/>
27             </td>
28           </tr>
29         </table>
30       </body>
31     </html>
32   </xsl:template>

Ligne 20, nous démarrons un tableau, et dans la première colonne, nous mettons le code du menu, ligne 23. Ligne 26, dans la seconde colonne, nous retrouvons ce que nous avions avant.

Vous pouvez essayer de générer une nouvelle page avec ceci: vous verrez apparaître sur la gauche le mot "menu", que nous allons maintenant remplacer par le vrai menu, celui de menu.xml.

Prenez note que j'ai mis une classe à la balise <table> afin de pouvoir changer facilement son style ensuite, via la feuille de style CSS. Ici, nous restons fonctionnels, et le sujet est traité dans l'article sur les feuilles de style CSS de ce même dossier.

5.5. Utilisation de menu.xml

Nous allons maintenant utiliser menu.xml et à partir de maintenant, tous les changements se font dans le modèle menu. Voici ce que vous pouvez faire...

1   <xsl:template name="menu" mode="menu">
2     <p class='menu'>
3       <xsl:variable name="menu" select="document('menu.xml')/menu"/>
4         <span class='menuitem'>[
5 	  <xsl:value-of select="./item[@name='download']/text[@lang=$lang]"/>
6 	]</span>
7     </p>
8   </xsl:template>

Dans ce nouvel exemplaire du modèle, j'ai introduit deux notions, outre ce qui concerne XPath. La première notion est la variable menu. Elle est déclarée avec la balise <xsl:variable name="menu">. Son contenu est le résultat de la requête XPath contenue dans l'attribut select. Ici, je vais détailler un peu la fonction XPath document() charge le fichier dont le nom est fourni en argument, et comme je spécifie ensuite /menu, la variable name va contenir tous les noeuds fils du noeud <menu> de menu.xml.

L'autre notion introduite ici est de mettre le résultat d'une recherche XPath (donc d'une requète aussi bien que d'un calcul) dans l'arbre généré en sortie. Nous utilisons la balise <xsl:value-of>, et c'est le résultat de ce qui est spécifié dans l'attribut select qui sera utilisé. Dans notre cas, ce sera donc le texte dont la balise lang correspond à la langue choisie pour la page web en cours de génération, pour l'item dont le nom est download. Pour simplifier l'exemple, je n'ai mis que cet item, mais il est évident que vous devriez dupliquer cette balise autant de fois qu'il y a d'items.

5.6. Une boucle avec XSLT

Dupliquer cette balise autant de fois qu'il y a d'items, cela ressemble fortement à un besoin de boucle, pour ceux qui programment. La notion de boucle existe avec XSLT: c'est la balise <xsl:for-each> qu'il faut utiliser, et encore une fois, c'est l'attribut select qui détermine la liste sur laquelle on va boucler. Voici ce que devient le modèle avec la boucle:

 1   <xsl:template name="menu" mode="menu">
 2     <p class='menu'>
 3       <xsl:variable name="menu" select="document('menu.xml')/menu"/>
 4       <xsl:for-each select="$menu/item">
 5         <span class='menuitem'>[
 6 	  <xsl:value-of select="./text[@lang=$lang]"/>
 7 	]</span>
 8 	<br/>
 9       </xsl:for-each>
10     </p>
11   </xsl:template>

Ici, la seule nouveauté est ligne 4, qui indique que tout ce qui se trouve entre les balises ouvrante et fermante de la boucle va être appliqué pour tous les éléments sélectionnés par $menu/item, donc si on regarde menu.xml, pour tous les items. Il reste donc, pour chaque item, à selectionner ce que l'on veut ligne 6, avec la balise <xsl:value-of> que l'on connaît déjà, mais où le noeud courant n'est plus défini dans menu.xml par la balise <menu> mais par un de ses fils <item>. Par ailleurs, remarquez encore une fois la classe CSS que j'ai placée aux bons endroits pour une utilisation ultérieure éventuelle via la feuille de style CSS.

5.7. Ajout d'une URL

Nous avons mis en place le menu, mais ce serait quand même plus agréable d'avoir des URL plutôt que des noms. Voici comment mettre une URL HTML dans une feuille de style XSLT:

1   <xsl:element name="a">
2     <xsl:attribute name="href">URL</xsl:attribute>
3     lien
4   </xsl:element>

Ceci insère le code HTML suivant: <a href="URL">lien</a>. Dans la version précédente de notre modèle menu, nous remplaçons donc la ligne 6 par ceci:

1           <xsl:element name="a">
2 	    <xsl:attribute name="href"><xsl:value-of select="@name"/>.<xsl:value-of select="$lang"/>.html</xsl:attribute>
3             <xsl:value-of select="./text[@lang=$lang]"/>
4 	  </xsl:element>

Ceci remplace donc notre item par une URL dont le texte est toujours l'item, et dont l'URL est composée de l'attribut name de la balise <item>, de la langue, et de l'extention .html. Pour la page espagnole de téléchargement, cela donne donc download.es.html.

5.8. Un test avec XSLT

Enfin, pour terminer cette page, nous allons rajouter un petit élément bien sympathique: l'item est une URL sauf si la page générée est celle de l'item. Pour cela, le processeur XSLT doit connaître le nom du fichier xhtml qu'il est en train d'utiliser en tant que source. Et cela n'est pas possible puisqu'à priori, il n'utilise que son contenu. Nous allons donc utiliser l'astuce suivante: ce sera un paramètre, tout comme la langue! Ensuite, le test consiste à dire que si l'item correspond au paramètre, on affiche du texte, et sinon, on affiche une URL.

Pour faire un test en XSLT, on utilise la balise <xsl:if>. Son attribut test permet de placer une condition. Ce qui se trouve entre la balise ouvrante et la balise fermante est traité si la condition est vraie, et ignoré sinon. L'inconvénient de <xsl:if> est que contrairement aux langages de programmations évolués comme le C ou meme moins évolués comme le java ou le C++ (!), nous n'avons rien pour exprimer un sinon comme le else de ces deux langages. Du coup, il faudrait que l'on fasse un test, si la condition est respectée, on met le lien, puis, si non(condition) est respectée, on met juste le texte. Cela est lourd et on va s'en sortir autrement.

Comme dans un langage digne de ce nom comme le C ou le lisp (dont XSLT s'inspire d'ailleurs), nous avons aussi un jeu d'instructions cas 1/cas 2/...cas n/cas par défaut. En XSLT, voici ce que cela donne:

  1   <xsl:choose>
  2     <xsl:when test="condition 1">
  3       Cas 1
  4     </xsl:when>
  5     <xsl:when test="condition 2">
  6       Cas 2
  7     </xsl:when>
...     ...
  n     <xsl:otherwise>
n+1       Cas par défaut
n+2     </xsl:otherwise>
n+3   </xsl:choose>

Nous allons donc utiliser cela, avec une seule condition, et le <xsl:otherwise>. Voici la version définitive de notre modèle menu:

 1   <xsl:template name="menu" mode="menu">
 2     <p class='menu'>
 3       <xsl:variable name="menu" select="document('menu.xml')/menu"/>
 4       <xsl:for-each select="$menu/item">
 5         <span class='menuitem'>[
 6 	  <xsl:choose>
 7 	    <xsl:when test='$filename=@name'>
 8 	      <xsl:value-of select="./text[@lang=$lang]"/>
 9 	    </xsl:when>
10 	    <xsl:otherwise>
11               <xsl:element name="a">
12 	        <xsl:attribute name="href"><xsl:value-of select="@name"/>.<xsl:value-of select="$lang"/>.html</xsl:attribute>
13                 <xsl:value-of select="./text[@lang=$lang]"/>
14 	      </xsl:element>
15 	    </xsl:otherwise>
16 	  </xsl:choose>
17 	]</span>
18 	<br/>
19       </xsl:for-each>
20     </p>
21   </xsl:template>

Il n'y a rien à commenter sur cette version définitive vu que tout ce qu'il n'y a rien de nouveau par rapport à ce qui précède.

création est mise à disposition sous un contrat Creative Commons