<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Tutoriel</title>
  <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/taxonomy/term/1084"/>
  <link rel="self" type="application/atom+xml" href="http://artisan.karma-lab.net/taxonomy/term/1084/atom/feed"/>
  <id>http://artisan.karma-lab.net/taxonomy/term/1084/atom/feed</id>
  <updated>2008-09-29T11:37:36+02:00</updated>
  <entry>
    <title>Débugger une application PHP</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1670" />
    <id>http://artisan.karma-lab.net/node/1670</id>
    <published>2008-10-05T22:39:10+02:00</published>
    <updated>2008-10-05T22:39:10+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Développement" />
    <category term="drupalfr.org" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
   Debugger une application PHP se résume souvent à coller des <kbd>error_log</kbd> un peu partout dans le code. Mais lorsque le dit code commence à passer une taille critique, cette option devient vite ingérable. C'est là qu'intervient <a class='external' target='_blank' href='http://www.xdebug.org/' >Xdebug</a>, le concurrent libre du debugger de Zend. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Debugger une application PHP se résume souvent à coller des <kbd>error_log</kbd> un peu partout dans le code. Mais lorsque le dit code commence à passer une taille critique, cette option devient vite ingérable. C'est là qu'intervient <a class='external' target='_blank' href='http://www.xdebug.org/' >Xdebug</a>, le concurrent libre du debugger de Zend. 
</p>
<!--break-->

	<a name='chapter_1'></a>
	<h2>Le Pas à pas</h2>
	
<p>
  Pour traquer les cafards, nous allons tout d'abord utiliser <a class='external' target='_blank' href='http://www.eclipse.org/' >Eclipse</a> et l'excellent plugin <a class='external' target='_blank' href='/node/1262' >PDT</a>. Pour l'instant, pour le développement PHP, j'utilise conjointement PDT 1.x et Eclipse 3.3 (Europa) en attendant que PDT 2.0 se stabilise et puisse fonctionner correctement avec Eclipse 3.4 (Ganymede). 
</p>
<p>
  Une fois le plugin installé dans eclipse avec l'url de mise à jour <kbd>http://download.eclipse.org/tools/pdt/updates/</kbd>, il reste à ajouter Xdebug à la votre <a class='external' target='_blank' href='/node/1055' >plateforme de test</a>. Je pars du principe qu'Apache et PHP sont déjà installé et que le serveur est en écoute sur <kbd>localhost</kbd>.
</p>
<p>
  Il y a de très fortes chances que <kbd>xdebug</kbd> soit déjà inclus dans votre distribution. Sous Mandriva, cela passe donc simplement par un <kbd>urpmi php-xdebug</kbd>. Sur les debian-like j'imagine qu'un coup d'<kbd>apt-get</kbd> ne doit pas tomber très loin. 
</p>
<p>
  Une fois le paquet en place, reste à configurer PHP pour qu'il utilise cette nouvelle extension. Toujours sous Mandriva, cela se passe dans le fichier <kbd>/etc/php.d/A29_xdebug.ini</kbd>. Là nous allons rechercher une série de variables suivantes à modifier :
  <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">xdebug.remote_enable <span class="sy0">=</span><span class="re2"> On</span></div></li>
<li class="li1"><div class="de1">xdebug.remote_host <span class="sy0">=</span><span class="re2"> localhost</span></div></li>
<li class="li1"><div class="de1">xdebug.remote_port <span class="sy0">=</span><span class="re2"> <span class="nu0">9000</span></span></div></li>
<li class="li1"><div class="de1">xdebug.remote_handler <span class="sy0">=</span> <span class="st0">&quot;dbgp&quot;</span></div></li></ol></div></div></div>
</p>
<p>
  Ici nous avons activé le debugger à distance,indiqué qu'il devait se connecter sur <kbd>localhost9000</kbd>, au port <kbd>9000</kbd> (nous verons pourquoi plus loin), en utilisant le protocole de communication <kbd>dbfp</kbd> (et non plus l'ancien <kbd>dbg</kbd>). 
</p>

<div class='inline-box note'>
Je n'ai pas noté de perte significative de performance en laissant ces options ainsi même lorsque l'on ne debugge pas le code. Peut-être d'autres ont une expérience différente ?
</div>

<p>
  Ceci fait, un coup de redémarrage d'apache et nous pouvons retourner sous Eclipse. 
</p>

<p>
  Là encore, quelques réglages sont nécessaires. Déjà il nous faut configurer un navigateur par défaut. Pour cela, faite un tour dans <kbd>Windows</kbd>/<kbd>Préférences</kbd> (me demandé pas pourquoi ils ont clocké ça là dedans...), <kbd>General</kbd>/<kbd>Web Browser</kbd>. Là vous ajouterez votre navigateur préféré en prendrez soin de le cocher une fois l'opération réalisée pour le rendre actif par défaut. 
</p>
<p>
  Ne partez pas tout de suite, et allez dans la section <kbd>PHP</kbd>/<kbd>Debug</kbd>. Là vous pouvez sélectionner <kbd>xdebug</kbd> et clicker sur <kbd>Configure</kbd>, histoire de vérifier que le host est bien <kbd>localhost</kbd> et le port <kbd>9000</kbd>, comme nous l'avions configuré pour PHP.
</p>
<p>
  Voilà, vous pouvez clicker sur <kbd>OK</kbd> pour valider ce paramétrage.</p>
<p> 
  Maintenant, il ne nous reste plus qu'à lancer l'application en mode déverminage par <kbd>Run</kbd>/<kbd>Debug As</kbd>/<kbd>PHP Web Page</kbd>. Comme c'est un nouveau lancement, on vous demande une URL qui est généralement fausse. Remplacez cela par celle de la page à tester. Une fois validée, le navigateur s'ouvre et l'exécution s'arrête brutalement sur le première ligne du premier script PHP. Après c'est du classique, vous avez accès au pas à pas (F6), aux points d'arrêt, à la visualisation des variables par survol, l'ajout d'expressions <kbd>watch</kbd>, etc...
</p>



	<a name='chapter_2'></a>
	<h2>Analyse de performances</h2>
	
<p>
  L'intérêt de <kbd>Xdebug</kbd> ne s'arrête pas au debuggage, loin de là. C'est aussi un formidable outil d'analyse de performance (profiling). Pour activer ce mode, il faut retourner dans le fichier de configuration de <kbd>Xdebug</kbd> pour modifier comme suit :
  <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">xdebug.profiler_append <span class="sy0">=</span><span class="re2"> Off</span></div></li>
<li class="li1"><div class="de1">xdebug.profiler_enable <span class="sy0">=</span><span class="re2"> On</span></div></li>
<li class="li1"><div class="de1">xdebug.profiler_enable_trigger <span class="sy0">=</span><span class="re2"> Off</span></div></li>
<li class="li1"><div class="de1">xdebug.profiler_output_dir <span class="sy0">=</span><span class="re2"> /tmp</span></div></li>
<li class="li2"><div class="de2">xdebug.profiler_output_name <span class="sy0">=</span><span class="re2"> php_profile.txt</span></div></li></ol></div></div></div>
</p>
<p>
<image file="kcachegrind.png"/>
  Attention, cette fois il est très claire qu'activer cela nuit gravement aux performances. Activez et désactivez donc <kbd>xdebug.profiler_enable</kbd> selon vos besoins. 
</p>

<p>
  Une fois le serveur Apache redémarré, il ne vous reste plus qu'à aller faire un tour sur la page qui explose les compteurs de temps. Quant vous avez terminé, retournez voir dans le dossier <kbd>/tmp</kbd> où devrait se trouver un <kbd>php_profile.txt</kbd>, c'est lui qui contient tous les temps d'exécution enregistrés par <kbd>Xdebug</kbd>.
</p>
<p>
  Pour visualiser le résultat, malheureusement il n'existe pas (à ma connaissance) d'application GTK. Mais il y a néanmoins le très bon <kbd>kcachegrind</kbd> qui permet une exploration précise et rapide des temps d'exécutions. 
</p>



	<a name='chapter_3'></a>
	<h2>Conclusion</h2>
	
<p>
  <kbd>Xdebug</kbd> fait à mon sens parti de la trousse à outil de base lorsque l'on porte sa casquette de développeur (ou pompier <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/> PHP. Il n'empêche pas de coller nos <kbd>error_log</kbd> de base mais aide énormément lorsque l'application patine ou qu'un algo part en vrille. 
</p>

    ]]></content>
  </entry>
  <entry>
    <title>Créer un environnement de test &#039;la(p|m)p&#039; avec Chroot</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1055" />
    <id>http://artisan.karma-lab.net/node/1055</id>
    <published>2008-10-03T12:31:12+02:00</published>
    <updated>2008-10-03T12:33:35+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Serveurs" />
    <category term="drupalfr.org" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
   Lorsque l'on a besoin de créer un environnement de test il est nécessaire que sa composition soit strictement contrôlé (paramétrages, applications et services disponibles et lancées, etc). Il n'est du coup généralement pas conseillé d'utiliser sa machine de travail, sauf si elle ne sert qu'à cela, sous peine d'en détériorer le fonctionnement en cas de test malheureux ou de modifier sans le vouloir le comportement de ce que l'on cherche à tester. Nous sommes alors contraints d'avoir soit une machine virtuelle dédiée aux tests, soit une machine physique supplémentaire. 
</p>
<p>
  Ce serait sans compter sur la commande <kbd>chroot</kbd> qui permet dans certaines conditions d'obtenir un environnement de test ou d'intégration identique à celui en production, sans machine physique supplémentaire et sans que la machine principale soit ralentie ou compromise. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Lorsque l'on a besoin de créer un environnement de test il est nécessaire que sa composition soit strictement contrôlé (paramétrages, applications et services disponibles et lancées, etc). Il n'est du coup généralement pas conseillé d'utiliser sa machine de travail, sauf si elle ne sert qu'à cela, sous peine d'en détériorer le fonctionnement en cas de test malheureux ou de modifier sans le vouloir le comportement de ce que l'on cherche à tester. Nous sommes alors contraints d'avoir soit une machine virtuelle dédiée aux tests, soit une machine physique supplémentaire. 
</p>
<p>
  Ce serait sans compter sur la commande <kbd>chroot</kbd> qui permet dans certaines conditions d'obtenir un environnement de test ou d'intégration identique à celui en production, sans machine physique supplémentaire et sans que la machine principale soit ralentie ou compromise. 
</p>
<!--break-->

	<a name='chapter_6'></a>
	<h2>Qu'est-ce qu'un chroot?</h2>
	
<p>
  Comme vous le savez sûrement déjà, le système fichier d'un *NIX est construit autour d'une racine (le <kbd>/</kbd>) sur laquelle les partitions sont ensuite "montées" formant ainsi l'espace de fichier accessible. Ce que l'on sait moins c'est que cette racine est un paramètre du processus. Tout processus lancé peut avoir pour racine un chemin arbitraire issu de celle de son processus parent. Si ce paramètre n'est pas définit, ce qui est généralement le cas, c'est '/' qui est utilisé par défaut, en quelque sorte la racine de la racine du parent. La commande <kbd>chroot</kbd> permet de lancer une commande et dans la foulée de définir ce paramètre pour le processus engendré. 
</p>
<p>
  La commande <kbd>chroot</kbd> peut donc avoir deux paramètres principaux : un chemin dans l'arborescence et une commande. Ainsi, si en tant qu'utilisateur <kbd>root</kbd> nous lançons <kbd>chroot /lapp /bin/bash</kbd>, voilà ce qui se passe :
  <ol>
   <li>La commande <kbd>chroot</kbd> engendre un processus qui a pour racine la même que celle de son processus parent, généralement le <kbd>/</kbd> (mais rien n'empêche de faire des poupées russes...).</li>
   <li>En interne, <kbd>chroot</kbd> appel de la fonction kernel <kbd>chroot("/lapp")</kbd>. Le kernel va donc modifier la valeur de la racine pour ce processus et lui associer la valeur <kbd>/lapp</kbd>.</li>
   <li>Le processus de <kbd>chroot</kbd>, exécute la commande passée en 2nd paramètre, <kbd>/bin/bash</kbd>. Comme la racine de <kbd>chroot</kbd> a été changée, c'est en quelque sorte bien le <kbd>/bin/bash</kbd> à partir de la nouvelle racine <kbd>/lapp</kbd> qui va être exécuté.</li>
   <li>Cette exécution débouche sur la création d'un processus fils de celui du <kbd>chroot</kbd> qui hérite donc de cette nouvelle racine.</li>
   <li>Tout ce que <kbd>/bin/bash</kbd> lancera par la suite héritera de cette nouvelle racine jusqu'à ce que l'on tape <kbd>exit</kbd> qui mettra fin au processus <kbd>/bin/bash</kbd>, et par domino à celui du <kbd>chroot</kbd> qui a permis son lancement.
  </ol>
</p>

<p>
  Le premier constat que nous pouvons tire de cela est qu'il faut que notre dossier <kbd>/lapp</kbd> contienne un <kbd>bin/bash</kbd>, mais aussi toutes les librairies dont a besoin <kbd>bash</kbd> pour fonctionner, ainsi que tous les dossiers systèmes qui lui sont nécessaire et qu'il s'attend à trouver comme sous une "vraie" racine. Ainsi la seule problématique posée sera le peuplement de notre nouvelle racine. 
</p>
<p>
  Le second constat est qu'un chroot <strong>n'est pas une virtualisation</strong>. Une virtualisation définit des conteneurs bien hermétiques avec leur mémoire propre, leur espace disque propre, leurs périphériques propres, etc. Le chroot ne fait rien de tout cela. La mémoire, le CPU, les périphériques (réseau, disque, écran, etc) sont partagées par l'environnement racine et chrooté. C'est d'ailleurs pour cela qu'un chroot est aussi rapide que si tout était lancé sur la racine principale. 
</p>
<p>
  Chroot ne fait donc que "faire croire" à un processus qu'il travaille sous la "vraie racine" alors qu'il n'est que dans un sous-dossier. Ainsi la seule garantie que fournit le <kbd>chroot</kbd> est l'<strong>étanchéité des fichiers</strong> dans le sens enfant-parent. C'est pour cet aspect que le <kbd>chroot</kbd> est d'ailleurs généralement utilisé, pour "mettre en prison" des applications critiques (serveur web, serveur mail, etc.) de sorte à ce que si une vulnérabilité permet à un cracker de casser le serveur, il ne puisse atteindre les fichiers se trouvant hors du chroot et ainsi limiter les dégâts. A noter que ce billet <strong>ne traite pas</strong> de cette utilisation critique du chroot et que d'un point de vue général il existe nombre d'exploits permettant de s'échapper de cette prison. 
</p>

<p>
  Malgré ses limitations, le <kbd>chroot</kbd> est juste parfait pour une utilisation : la création d'un environnement de test et/ou d'intégration pour des applications WEB. Il permet en effet très facilement de créer une configuration LA(P|M)P (Linux-Apache-Postgres/MySQL-PHP), facile à maintenir, facile à recréer, testable de la machine principale sans modification particulière comme si tout était installé "localement". 
</p>


	<a name='chapter_7'></a>
	<h2>Mise en oeuvre</h2>
	
<p>
  Notre but est donc ici de créer un environnement chrooté comprenant un Linux de base, Apache, Postgresql et PHP. La première étape est donc de créer notre future racine. Je le fait en <kbd>/lapp</kbd> mais cela pourrait être virtuellement n'importe où, y compris sur le dossier d'une clef mémoire ou sur un espace volatile type <kbD>tmpfs</kbd>. 
<div class='code-container-area'><div class='code-container'><div class="code"><span class="kw2">sudo</span> <span class="kw2">mkdir</span> <span class="sy0">/</span>lapp</div></div></div>
</p>
<p>
  Maintenant vient le moment de peupler notre racine. Là, il y a trois approches possibles. 
</p>
<p>
  Nous avons l'approche dite "bourrine" consistant à dupliquer sauvagement son environnement principal :
<div class='code-container-area'><div class='code-container'><div class="code"><span class="kw2">cp</span> -a <span class="sy0">/</span>usr <span class="sy0">/</span>bin <span class="sy0">/</span>lib <span class="sy0">/</span>sbin <span class="sy0">/</span>var <span class="sy0">/</span>lapp</div></div></div>
</p>
<p>
  L'inconvénient est que c'est souvent très volumineux et peu adapté à un environnement de test contrôlé dans la mesure où des tas de choses inutiles sont injectées dans la nouvelle racine. 
</p>
<p>
  Ensuite il y a l'approche "minimaliste" qui cherche à ne copier que ce qui est nécessaire. Cela peut se faire à la main avec la commande <kbd>ldd</kbd> ou beaucoup plus simplement, comme me l'a indiqué Guyou, avec les outils <a class='external' target='_blank' href='http://www.floc.net/makejail/' >makejail</a> ou <a class='external' target='_blank' href='http://freshmeat.net/projects/chrootbin/' >chrootbin</a>. Mais cette technique est plus adaptée au lancement d'un serveur dans une prison chroot qu'à notre besoin. 
</p>
<p>
  Enfin nous avons l'approche "raisonnable" consistant à utiliser les outils fournis par les debian's et autres mandriva's qui permettant d'installer tout simplement sur une racine arbitraire une distribution complète. C'est clairement dans notre cas cette approche qui s'impose. 
</p>
<p>
  Sous mandriva (et distributions dérivées), l'installation d'un linux de base sur une racine se fait simplement par la commande suivante :
<div class='code-container-area'><div class='code-container'><div class="code">&nbsp; <span class="kw2">sudo</span> urpmi basesystem urpmi locales-fr --root <span class="sy0">/</span>lapp</div></div></div>
</p>

<p>
  Simple et diablement efficace. Comme vous l'aurez noté, ce sont trois paquets qui sont ici installés: le linux de base, la langue française et le système de gestion de paquets. Ce dernier point nous offrira beaucoup de souplesse car au sein même du chroot il sera du coup possible d'installer, désinstaller ou même mettre à jour la distribution.
</p>
<div class='inline-box note'>
  A noter que ce n'est pas du "one shot", il est toujours possible d'ajouter des paquets à la racine "de l'exterieur" par :
<div class='code-container-area'><div class='code-container'><div class="code"><span class="kw2">sudo</span> urpmi un_paquer --root <span class="sy0">/</span>lapp</div></div></div>
</div>

<p>
  Les distributions basées sur debian disposent elle aussi d'une méthode très efficace pour créer une racine minimale en utilisant cette fois la commande <kbd>debootsrap</kbd>. 
<div class='code-container-area'><div class='code-container'><div class="code">&nbsp; <span class="kw2">sudo</span> debootstrap --arch i386 &nbsp;--<span class="re2">include=</span>locales-all &nbsp;sid <span class="sy0">/</span>lapp http:<span class="sy0">//</span><span class="kw2">ftp</span>.fr.debian.org<span class="sy0">/</span>debian</div></div></div>
</p>

<p>
  Vous pouvez remplacer <kbd>sid</kbd> par toute distribution debian disponible (etch, sarge, etc..). Pour le reste cela fonctionne comme la méthode précédente a la différence que la gestion de paquet n'est pas optionnelle et vous aurez donc automatiquement accès à apt-get dans le chroot. 
</p>

<div class='inline-box note'>
   Un détail intéressant est que la commande <kbd>debootstrap</kbd> est aussi disponible sous Mandriva et vous permet sans aucun problème, d'installer une racine de debian dans une mandriva. 
</div>


<p>
  Notre racine étant maintenant peuplée et les utilitaires que nous avons utilisé ont normalement pris soin de créer à la "racine" les dossier vitaux <kbd>tmp</kbd>, <kbd>var</kbd>, <kbd>dev</kbd>, <kbd>sys</kbd> et <kbd>proc</kbd>. Si ce n'est pas le cas, faites le vous-même. 
</p>

<p>
  Maintenant comme vous le savez, certains dossiers de Linux ne sont pas de "vrais" dossier mais une vue sur des variables du système. C'est le cas de <kbd>/proc</kbd>, <kbd>/sys</kbd> et <kbd>/dev</kbd>. Si nous lancions maintenant notre nouvelle racine, ces dossiers seraient donc vides provoquant des erreurs sur certaines fonctions. Il nous faut donc avant monter ces dossiers. Pour ce faire, nous allons simplement les "relier" (mount --bind) aux mêmes dossiers de la vraie racine.
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="kw2">sudo</span> <span class="kw2">mount</span> -bind <span class="sy0">/</span>dev <span class="sy0">/</span>lapp<span class="sy0">/</span>dev </div></li>
<li class="li1"><div class="de1"><span class="kw2">sudo</span> <span class="kw2">mount</span> -bind <span class="sy0">/</span>proc <span class="sy0">/</span>lapp<span class="sy0">/</span>proc</div></li>
<li class="li1"><div class="de1"><span class="kw2">sudo</span> <span class="kw2">mount</span> -bind <span class="sy0">/</span>sys <span class="sy0">/</span>lapp<span class="sy0">/</span>sys</div></li></ol></div></div></div>
</p>

<p>
  Concernant le réseau dans un chroot, la configuration sera la même que celle de l'environnement parent. Tout fonctionnera donc sans avoir à faire quoi que ce soit. En revanche il est important de vérifier que la résolution de noms se fait correctement. Pour cela nous allons simplement recopier le fichier <kbd>/etc/resolv</kbd> :
<div class='code-container-area'><div class='code-container'><div class="code"><span class="kw2">cp</span> <span class="sy0">/</span>etc<span class="sy0">/</span>resolv.conf <span class="sy0">/</span>lapp<span class="sy0">/</span>etc<span class="sy0">/</span>resolv.conf</div></div></div>
</p>

<p>
Maintenant tout est en place, c'est le moment du lancement. 
<div class='code-container-area'><div class='code-container'><div class="code"><span class="kw2">sudo</span> <span class="kw2">chroot</span> <span class="sy0">/</span>lapp <span class="sy0">/</span>bin<span class="sy0">/</span>bash</div></div></div>
</p>

<p>
  Voilà, une fois ceci exécuté, nous sommes "prisonnier" de la nouvelle racine et toutes les commandes qui suivent sont contrainte dans cet espace. Pour en sortir il suffit de faire un "exit". Comme vous le voyez tout fonctionne, vous pouvez utiliser le réseau, installer des paquets, etc. C'est ce que nous allons faire pour mettre en place notre environnement de test :
<div class='code-container-area'><div class='code-container'><div class="code">urpmi apache-mod_php postgresql-server</div></div></div>
</p>

<p>
  Ceci fait, vous pouvez constater que votre système fonctionne en lançant les services :
  <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>httpd start</div></li>
<li class="li1"><div class="de1"><span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>postgresql start</div></li></ol></div></div></div>
</p>
<p>
  Maintenant, en allant, sur votre environnement principal aidé d'un navigateur web sur l'adresse <kbd>http://localhost</kbd>, vous devriez avoir accès à la page de test du serveur Apache. Pour le reste, à vous de jouer...
</p>


	<a name='chapter_8'></a>
	<h2>Automatisation</h2>
	
<p>
   Comme vous l'aurez constaté, le chroot n'effectue pas, et heureusement, un démarrage réel du linux installé à la nouvelle racine. Il se contente d'exécuter <kbd>/bin/bash</kbd> ce qui vous oblige de votre côté à lancer les services à la main. 
</p>
<p>
  Pour rendre cela beaucoup plus simple à utiliser sur une base quotidienne, le plus propre reste de créer un service dédié au lancement de notre racine qui va créer le montages et lancer apache, postgres, etc... Son arrêt ferra l'inverse :
  <div class='code-container-area'><div class='code-container-caption'>/etc/init.d/lapp</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="co0">#!/bin/sh</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># chkconfig: 345 56 50</span></div></li>
<li class="li1"><div class="de1"><span class="co0"># description: This startup script launches Chroot Starter</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0">### BEGIN INIT INFO</span></div></li>
<li class="li1"><div class="de1"><span class="co0"># Provides: lappd</span></div></li>
<li class="li1"><div class="de1"><span class="co0"># Required-Start: </span></div></li>
<li class="li1"><div class="de1"><span class="co0"># Required-Stop: </span></div></li>
<li class="li2"><div class="de2"><span class="co0"># Default-Start: 345</span></div></li>
<li class="li1"><div class="de1"><span class="co0"># Short-Description: Chroot Starter</span></div></li>
<li class="li1"><div class="de1"><span class="co0"># Description: This startup script launches Chroot Starter</span></div></li>
<li class="li1"><div class="de1"><span class="co0">### END INIT INFO</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2"><span class="re2">PATH=</span><span class="sy0">/</span>usr<span class="sy0">/</span>sbin:<span class="sy0">/</span>usr<span class="sy0">/</span>bin:<span class="sy0">/</span>sbin:<span class="sy0">/</span>bin</div></li>
<li class="li1"><div class="de1"><span class="re2">NAME=</span>lapp</div></li>
<li class="li1"><div class="de1"><span class="re2">DESC=</span><span class="st0">&quot;Chroot Starter for $NAME&quot;</span></div></li>
<li class="li1"><div class="de1"><span class="re2">ROOT=</span><span class="sy0">/</span><span class="re1">$NAME</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2"><span class="co0"># Source function library.</span></div></li>
<li class="li1"><div class="de1">. <span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>functions</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw1">case</span> <span class="st0">&quot;$1&quot;</span> <span class="kw1">in</span></div></li>
<li class="li1"><div class="de1">&nbsp; start<span class="br0">&#41;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> -z <span class="st0">&quot;$(mount | grep $ROOT)&quot;</span> <span class="br0">&#93;</span> ; <span class="kw1">then</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; gprintf <span class="st0">&quot;$NAME is already started<span class="es0">\n</span>&quot;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">3</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">fi</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; gprintf <span class="st0">&quot;Starting chroot for %s&quot;</span> <span class="st0">&quot;$NAME&quot;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">mount</span> --bind <span class="sy0">/</span>proc <span class="re1">$ROOT</span><span class="sy0">/</span>proc</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">mount</span> --bind <span class="sy0">/</span>dev <span class="re1">$ROOT</span><span class="sy0">/</span>dev</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">mount</span> --bind <span class="sy0">/</span>sys <span class="re1">$ROOT</span><span class="sy0">/</span>sys</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">chroot</span> <span class="sy0">/</span>storage<span class="sy0">/</span>virtuals<span class="sy0">/</span>roots<span class="sy0">/</span><span class="re1">$NAME</span> <span class="sy0">/</span>init.<span class="kw2">sh</span> start</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; echo_success</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw3">echo</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">;;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; stop<span class="br0">&#41;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> -z <span class="st0">&quot;$(mount | grep $ROOT)&quot;</span> <span class="br0">&#93;</span> ; <span class="kw1">then</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; gprintf <span class="st0">&quot;$NAME is already stopped<span class="es0">\n</span>&quot;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">3</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">fi</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; gprintf <span class="st0">&quot;Stopping chroot for %s&quot;</span> <span class="st0">&quot;$NAME&quot;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">chroot</span> <span class="sy0">/</span>storage<span class="sy0">/</span>virtuals<span class="sy0">/</span>roots<span class="sy0">/</span><span class="re1">$NAME</span> <span class="sy0">/</span>init.<span class="kw2">sh</span> stop</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">umount</span> <span class="re1">$ROOT</span><span class="sy0">/</span>proc </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re4">$?</span> -ne <span class="nu0">0</span> <span class="br0">&#93;</span> ; <span class="kw1">then</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; echo_failure</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="kw3">echo</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">3</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">fi</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">umount</span> <span class="re1">$ROOT</span><span class="sy0">/</span>dev</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re4">$?</span> -ne <span class="nu0">0</span> <span class="br0">&#93;</span> ; <span class="kw1">then</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; echo_failure</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">echo</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">3</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">fi</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">umount</span> <span class="re1">$ROOT</span><span class="sy0">/</span>sys</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re4">$?</span> -ne <span class="nu0">0</span> <span class="br0">&#93;</span> ; <span class="kw1">then</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; echo_failure</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">echo</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">3</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">fi</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; echo_success</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw3">echo</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw3">exit</span> <span class="nu0">0</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">;;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; restart<span class="sy0">|</span>force-reload<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; $<span class="nu0">0</span> stop</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw2">sleep</span> <span class="nu0">1</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; $<span class="nu0">0</span> start</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">;;</span></div></li>
<li class="li2"><div class="de2"><span class="kw1">esac</span></div></li>
<li class="li1"><div class="de1"><span class="kw3">exit</span> <span class="nu0">0</span></div></li></ol></div></div></div>
</p>
<p>
  Ensuite dans notre racine il faut rajouter, et rendre exécutable, un fichier <kbd>/init.sh</kbd> contenant le démarrage/arrêt des services <kbd>httpd</kbd> et <kbd>postgresql</kbd>, et qui prendra en paramètre <kbd>start</kbd> ou <kbd>stop</kbd> :
  <div class='code-container-area'><div class='code-container-caption'>/init.sh</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="co0">#! /bin/sh </span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>httpd $<span class="nu0">1</span></div></li>
<li class="li1"><div class="de1"><span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>postgresql $<span class="nu0">1</span></div></li></ol></div></div></div>
</p>
<p>
  Après, vous pouvez rendre le lancement de ce service automatique (avec <kbd>chkconfig</kbd> ou l'outil dédié à cela pour votre distribution), ou faire cela à la main :
  <div class='code-container-area'><div class='code-container-caption'>exemple d&#039;utilisation</div><div class='code-container'><div class='traces'><ul><li class='result'># lancement du service</li><li><span class='prompt'></span><span class="kw2">sudo</span> <span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>lapp start</li><li class='result'>Starting chroot for lapp                                        [  OK  ]</li><li class='result'>&nbsp;</li><li class='result'># ouverture d'une session sur la racine</li><li><span class='prompt'></span><span class="kw2">sudo</span> <span class="kw2">chroot</span> <span class="sy0">/</span>lapp <span class="sy0">/</span>bin<span class="sy0">/</span><span class="kw2">sh</span></li><li class='result'># ...</li><li class='result'># exit</li><li class='result'>&nbsp;</li><li class='result'># extinction du service</li><li><span class='prompt'></span><span class="kw2">sudo</span> <span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>lapp stop</li><li class='result'>Starting chroot for lapp                                        [  OK  ]</li></ul></div></div></div>
</p>
<p>
  Personnellement j'ai mis ce service en démarrage manuel et ajouté un profile dans <kbd>gnome-terminal</kbd> qui me lance directement la commande <kbd>chroot /lapp /bin/sh</kbd>. 
</p>



	<a name='chapter_9'></a>
	<h2>Conclusion</h2>
	
<p>
La commande chroot ne manque donc pas d'intérêt en restant très simple à mettre en oeuvre si on en saisi bien les fondamentaux. C'est une bonne alternative à la "lourdeur" de la virtualisation et permet de créer à une vitesse record des environnements de développement rapides, efficaces et non-polluant pour le reste du système. 
</p>

    ]]></content>
  </entry>
  <entry>
    <title>Ajouter son propre automate de recheche à Firefox</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1661" />
    <id>http://artisan.karma-lab.net/node/1661</id>
    <published>2008-09-30T13:15:40+02:00</published>
    <updated>2008-09-30T16:30:21+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="FireFox" />
    <category term="drupalfr.org" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
   La zone de recherche de FireFox est bien pratique, difficile de remettre cela en cause. Son seul problème est qu'il n'y a étrangement aucun moyen en standard pour ajouter des requêtes un peu exotiques. Voici donc comment faire. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   La zone de recherche de FireFox est bien pratique, difficile de remettre cela en cause. Son seul problème est qu'il n'y a étrangement aucun moyen en standard pour ajouter des requêtes un peu exotiques. Voici donc comment faire. 
</p>
<!--break-->


	<a name='chapter_1'></a>
	<h2>Analyse de la requête Google</h2>
	
<p>
   Dans le temps, il existait une extension pour faire ce qui suit, <kbd>Open Search Fox</kbd>, mais elle n'a pas été portée avec FireFox 3.0 et tout semble indiquer qu'elle ne le sera pas. Alors même s'il existe une autre extension du même genre ou si quelqu'un se décide à porter celle-là, autant savoir faire sans. Ceci d'autant plus que c'est assez simple.
</p>
<p>
  Histoire de prendre un exemple, disons que je veuille utilisez les <a class='external' target='_blank' href='/node/1226' >fonctions étendues de Google</a> pour rechercher un projet Drupal particulier. Cela corresponds à taper dans Google <a class='external' target='_blank' href='http://www.google.fr/search?q=site%3Adrupal.org+inurl%3Aproject+webdav' >site:drupal.org inurl:project webdav</a>.
</p>
<p>
  Lorsque vous avez lancé cette requête, vous obtenez avec la page de résultat, dans la barre d'adresse, quelque chose commençant par <kbd>http://www.google.fr/search?q=site%3Adrupal.org+inurl%3Aproject+webdav</kbd>. Oubliez le reste, cela n'a pas d'importance. Nous constatons donc que l'URL de la recherche est <kbd>http://www.google.fr/search</kbd>, qu'il y a un paramètre nommé <kbd>q</kbd> et que sa valeur est <kbd>site%3Adrupal.org+inurl%3Aproject+webdav</kbd>. Dans cette valeur, <kbd>webdav</kbd> sera par la suite à remplacer par le nom du projet que l'on va effectivement rechercher. Le début sera toujours le même. 
</p>


	<a name='chapter_2'></a>
	<h2>Ajout de la requête</h2>
	
<p>
  Pour mettre cette requête dans Firfox, il faut commencer par trouver le dossier de votre profile, généralement <kbd>~/.mozilla/firefox/XXXXX.default</kbd>. Là se trouve le répertoire qui nous intéresse, <kbd>searchplugins</kbd>.
 Vous allez y créer un fichier <kbd>drupal-projects.xml</kbd> :
<div class='code-container-area'><div class='code-container-caption'>~/.mozilla/firefox/XXXXX.default/searchplugins/drupal-projects.xml</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sc3"><span class="re1">&lt;SearchPlugin</span> <span class="re0">xmlns</span>=<span class="st0">&quot;http://www.mozilla.org/2006/browser/search/&quot;</span><span class="re2">&gt;</span></span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;ShortName<span class="re2">&gt;</span></span></span>Drupal Projects<span class="sc3"><span class="re1">&lt;/ShortName<span class="re2">&gt;</span></span></span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;InputEncoding<span class="re2">&gt;</span></span></span>UTF-8<span class="sc3"><span class="re1">&lt;/InputEncoding<span class="re2">&gt;</span></span></span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;Image</span> <span class="re0">width</span>=<span class="st0">&quot;16&quot;</span> <span class="re0">height</span>=<span class="st0">&quot;16&quot;</span><span class="re2">&gt;</span></span>http://drupal.org/misc/favicon.ico<span class="sc3"><span class="re1">&lt;/Image<span class="re2">&gt;</span></span></span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sc3"><span class="re1">&lt;Url</span> <span class="re0">type</span>=<span class="st0">&quot;text/html&quot;</span> <span class="re0">method</span>=<span class="st0">&quot;GET&quot;</span> <span class="re0">template</span>=<span class="st0">&quot;http://www.google.fr/search&quot;</span><span class="re2">&gt;</span></span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;Param</span> <span class="re0">name</span>=<span class="st0">&quot;q&quot;</span> <span class="re0">value</span>=<span class="st0">&quot;site%3Adrupal.org+inurl%3Aproject+{searchTerms}&quot;</span><span class="re2">/&gt;</span></span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sc3"><span class="re1">&lt;/Url<span class="re2">&gt;</span></span></span></div></li>
<li class="li1"><div class="de1"><span class="sc3"><span class="re1">&lt;/SearchPlugin<span class="re2">&gt;</span></span></span></div></li></ol></div></div></div>
</p>
<p>
  Avant de redémarrer FireFox, quelques explications s'imposent. Déjà <kbd>ShortName</kbd> représente comme on se l'imagine le nom qui apparaîtra dans la liste déroulante de FireFox. <kbd>InputEncoding</kbd> permet de passer à Google une chaînes avec les accents correctement encodés. Et enfin <kbd>Image</kbd> pointe sur l'URL de l'icône qui va illustrer notre requête. 
</p>
<p>
  La partie intéressante arrive avec <kbd>Url</kbd>. Il s'agit d'invoquer la méthode <kbd>GET</kbd> sur une URL donnée par l'attribut <kbd>Template</kbd>. Cela implique qu'il est parfaitement possible d'utiliser la méthode <kbd>POST</kbd> qui peut être nécessaire pour certain sites qui utilisent des formulaires de recherche complexes. 
</p>
<p>
  Ici l'URL de base de la recherche est donc <kbd>http://www.google.fr/search</kbd>. Maintenant, il reste encore à envoyer à Google le paramètre, <kbd>q</kbd>, c'est le rôle de la balise <kbd>Param</kbd>.
</p>
<p>
  Comme nous l'avons vu plus haut, la valeur de <kbd>q</kbd> est composée de <kbd>site%3Adrupal.org+inurl%3Aproject+</kbd>, suivi du nom du projet à chercher. Ce nom est ici <kbd>{searchTerms}</kbd> qui indique à FireFox de placer là les mots que l'utilisateur a tapé avant de lancer la recherche. FireFox s'assure de la bonne conversion de la phrase, en convertissant, entre autre, les espaces par des +. 
</p>
<p>
  Et voilà, c'est tout. Redémarrez FireFox et la nouvelle recherche devrait apparaître prête à être utilisée.
</p>


	<a name='chapter_3'></a>
	<h2>Image embarquée</h2>
	
<p>
  Ici, nous donnons l'URL de l'icône dans la balise <kbd>Image</kbd>, mais il est aussi possible de directement intégrer cette image au fichier XML. Pour cela il faut encoder le fichier d'origine en un fragment de texte encodé base64. Pas évident. Heureusement, il existe de petits outils en ligne comme <a class='external' target='_blank' href='http://www.greywyvern.com/code/php/binary2base64' >ici</a>, à qui l'on donne l'URL et qui nous renvoient un bloc de texte à coller directement dans le contenu de la balise <kbd>Image</kbd>. Dans notre cas, nous utiliserons <kbd>http://drupal.org/misc/favicon.ico</kbd>. 
</p>


	<a name='chapter_4'></a>
	<h2>Conclusion</h2>
	
<p>
   Cette syntaxe n'est pas une exclusivité de FireFox. En réalité ce dernier ne fait qu'exploiter le format 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/OpenSearch'>
		  OpenSearch
		  </a> qui a été conçu par <a class='external' target='_blank' href='http://fr.wikipedia.org/wiki/A9.com' >A9</a>, une filiale d'Amazon. Ce format permet non seulement d'ajouter des automates de recherche à un navigateur comme nous l'avons fait ici, mais aussi, et surtout, donne à un site la possibilité d'indiquer à un navigateur le moteur de recherche qui lui est le plus adapté. Comme pour les flux Atom ou RSS, cela se fait très simplement en incluant dans chaque page du site une balise <kbd>link</kbd>, comme par exemple :
   <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&quot;search&quot;</span> <span class="re0">type</span>=<span class="st0">&quot;application/opensearchdescription+xml&quot;</span> <span class="re0">href</span>=<span class="st0">&quot;http://artisan.karma-lab.net/opensearch.xml&quot;</span> <span class="re0">title</span>=<span class="st0">&quot;Artisan Numérique&quot;</span> <span class="re2">/&gt;</span></span></div></li></ol></div></div></div>
</p>
<p>
   Du coup, lorsque le navigateur affiche cette page, il découvre cette balise et peut vous proposer d'ajouter ce nouveau moteur de recherche. Vous pouvez tester cela avec ce site. Allez dans la zone de recherche, déroulez la liste et vous devriez voir apparaître <kbd>Ajouter "Artisan Numérique"</kbd>. 
</p>

    ]]></content>
  </entry>
  <entry>
    <title>Créer un module Drupal : AHAH et formulaires dynamiques</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1653" />
    <id>http://artisan.karma-lab.net/node/1653</id>
    <published>2008-09-24T16:37:18+02:00</published>
    <updated>2008-09-29T21:47:38+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Drupal" />
    <category term="drupalfr.org" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
   Les formulaires dynamiques sont vieux comme le client-serveur. Cela peut correspondre par exemple à une liste principale dont le choix d'un élément déclenche la population d'une liste secondaire. Rien de bien sorcier donc, mais comme pour pas mal d'autres de choses, ce qui était relativement simple à coder avec un 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/RAD'>
		  RAD
		  </a> comme Delphi ou même Visual Basic, est devenu un véritable enfer avec la mode des applications WEB. Voyons donc comment faire ce type de chose avec la dernière <a class='external' target='_blank' href='http://api.drupal.org/api/file/developer/topics/forms_api_reference.html/6' >Form API de Drupal 6</a>.
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Les formulaires dynamiques sont vieux comme le client-serveur. Cela peut correspondre par exemple à une liste principale dont le choix d'un élément déclenche la population d'une liste secondaire. Rien de bien sorcier donc, mais comme pour pas mal d'autres choses, ce qui était relativement simple à coder avec un 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/RAD'>
		  RAD
		  </a> comme Delphi, ou même Visual Basic, est devenu un véritable enfer avec la mode des applications WEB. Voyons donc comment faire ce type de chose avec la dernière <a class='external' target='_blank' href='http://api.drupal.org/api/file/developer/topics/forms_api_reference.html/6' >Form API de Drupal 6</a>.
</p>
<!--break-->

	<a name='chapter_4'></a>
	<h2>Sources</h2>
	
<p>
   Les sources du module d'exemple courses sont disponibles <a class='external' target='_blank' href='/node/1654' >ici</a>.
</p>

	<a name='chapter_5'></a>
	<h2>Mise à jour du schéma</h2>
	
<p>
  Comme depuis un moment déjà nous allons continuer à torpiller notre <a class='external' target='_blank' href='/node/1561' >liste de courses</a> à qui nous avions récemment ajouté <a class='external' target='_blank' href='/node/1618' >les nouveaux schémas</a> de Drupal 6. 
</p>
<p>
  Pour les besoins de l'expérience, nous allons ajouter deux nouveaux champs à notre table <kbd>node_produit</kbd> : <kbd>categorie_produit</kbd> et <kbd>type_produit</kbd>. Le but est simple, lors de l'ajout ou de l'édition d'un produit, nous aurons une liste permettant de choisir la catégorie (féculents, jus de fruits, légumes, etc.). Et lorsque l'utilisateur sélectionnera un élément de cette liste, cela déclenchera la mise à jour d'une seconde liste de types de produit (pâtes, choux, jus d'orange, etc.). Un peu neu-neu, je sais, mais au moins on peut se concentrer sur la technique. 
</p>
<p>
   Pour commencer nous allons rapidement modifier le schéma de notre module (courses.install) en ajoutant nos deux champs :
   <div class='code-container-area'><div class='code-container-caption'>courses.install - courses_schema()</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'type_produit'</span> <span class="sy0">=&gt;</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'description'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st0">'Type du produit (pâtes, courgettes, etc...)'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'type'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st0">'int'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'unsigned'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'not null'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="br0">&#41;</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'categorie_produit'</span> <span class="sy0">=&gt;</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'description'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st0">'Catégorie de produit'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'type'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st0">'int'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'unsigned'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'not null'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="br0">&#41;</span><span class="sy0">,</span> &nbsp; </div></li></ol></div></div></div>
</p>

<p>
  Ensuite, il nous faut implémenter un nouveau <kbd>hook_update</kbd> pour permettre la mise à jour des anciens schémas :
   <div class='code-container-area'><div class='code-container-caption'>courses.install - courses_update_2()</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="kw2">function</span> courses_update_2<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re0">$ret</span> <span class="sy0">=</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; db_add_field<span class="br0">&#40;</span><span class="re0">$ret</span><span class="sy0">,</span> <span class="st0">'node_produit'</span><span class="sy0">,</span> <span class="st0">'categorie_produit'</span><span class="sy0">,</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'description'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st0">'Catégorie de produit'</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="st0">'type'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st0">'int'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'unsigned'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'not null'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; db_add_field<span class="br0">&#40;</span><span class="re0">$ret</span><span class="sy0">,</span> <span class="st0">'node_produit'</span><span class="sy0">,</span> <span class="st0">'type_produit'</span><span class="sy0">,</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="st0">'description'</span> &nbsp; <span class="sy0">=&gt;</span> <span class="st0">'Type du produit (pâtes, courgettes, etc...)'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'type'</span> &nbsp; &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="st0">'int'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'unsigned'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'not null'</span> &nbsp; &nbsp;<span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="kw1">return</span> <span class="re0">$ret</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span> &nbsp; </div></li></ol></div></div></div>
</p>
<p>
  Ceci fait, lancez la procédure de mise à jour de Drupal (update.php) au terme de laquelle, note table devrait être modifiée. 
</p>

	<a name='chapter_6'></a>
	<h2>Source de données</h2>
	
<p>
  Pour une véritable application, nos données <kbd>catégories</kbd> et <kbd>types</kbd> seraient proprement stockées en base de donnée. Ici, nous allons faire simple avec deux fonctions en dur :
  <div class='code-container-area'><div class='code-container-caption'>courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="kw1">function</span> courses_categories<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw3">return</span> array<span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="nu0">0</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">&quot;Catégorie du produit&quot;</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="nu0">1</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">&quot;Légumes&quot;</span><span class="br0">&#41;</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="nu0">2</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">&quot;Féculents&quot;</span><span class="br0">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="nu0">3</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">&quot;Jus de fruit&quot;</span><span class="br0">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2"><span class="kw1">function</span> courses_types<span class="br0">&#40;</span><span class="re1">$category</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; switch <span class="br0">&#40;</span><span class="re1">$category</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">1</span>: <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">return</span> array <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">1</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Choux'</span><span class="br0">&#41;</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="nu0">2</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Courgettes'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">3</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Carottes'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">2</span>: <span class="br0">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="kw3">return</span> array <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">1</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Spaghetties'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">2</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Penne Rigate'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">3</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">'Vermicelles'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">3</span>: <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="kw3">return</span> array <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">1</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">&quot;Jus d'orange&quot;</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="nu0">2</span>=<span class="sy0">&gt;</span>t<span class="br0">&#40;</span><span class="st0">&quot;Jus de pamplemousse&quot;</span><span class="br0">&#41;</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span> &nbsp; &nbsp;</div></li></ol></div></div></div>
</p>


	<a name='chapter_7'></a>
	<h2>Liste maître-esclave</h2>
	
<p>
  A l'ancienne mode, cela aurait consisté à utiliser le très vilain attribut de formulaire <kbd>DANGEROUS_SKIP_CHECK</kbd> et ajouter un bouton qui provoque une validation intermédiaire. Aujourd'hui trois arguments s'y opposent. Tout d'abord <kbd>DANGEROUS_SKIP_CHECK</kbd> a été supprimé. Ensuite les formulaires sont tous mis en cache et donc difficile à modifier dynamiquement. C'est ceci dit faisable en utilisant l'attribut de champ <kbd>#process</kbd> et de formulaire <kbd>#REBUILD</kbd> mais le fait de ne pas pouvoir by-passer les contrôles implique qu'à chaque mise à jour de la liste s'affiche des erreurs de validation, c'est moche. Enfin dernier argument, c'est pas AJAX donc c'est pas bien, on m'a dit...
</p>
<p>
  Le framework AHAH qui était un module pour Drupal 5, fait aujourd'hui parti du coeur de Drupal 6. Cette librairie utilise jQuery pour ajouter à Drupal cette giclée d'AJAX qui lui manquait temps.  La différence entre AJAX et AHAH (Asynchronous HTML over HTTP, me demandez pas pourquoi) est que le résultat de la réponse est du XHTML qui est directement collée dans le document en cours avec de petits effets genre glissement, fondus, etc...
</p>
<p>
 Il faut donc voir AHAH comme un sous-ensemble fonctionnel d'AJAX et cela va nous suffire car c'est exactement ce dont nous avons besoin. 
</p>
<p>
  L'intégration dans un formulaire d'AHAH est relativement directe. Pour nous deux listes, cela donne ceci (à placer à la tête de la fonction <kbd>courses_form</kbd> :
  
    <div class='code-container-area'><div class='code-container-caption'>courses.module - courses_form()</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="re1">$form</span><span class="br0">&#91;</span><span class="st0">'categorie_produit'</span><span class="br0">&#93;</span> = array<span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#type'</span> =<span class="sy0">&gt;</span> <span class="st0">'select'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#title'</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">'Catégorie'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#options'</span> =<span class="sy0">&gt;</span> courses_categories<span class="br0">&#40;</span><span class="br0">&#41;</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="st0">'#default_value'</span>=<span class="sy0">&gt;</span><span class="re1">$node</span>-<span class="sy0">&gt;</span>categorie_produit,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#description'</span> =<span class="sy0">&gt;</span> t<span class="br0">&#40;</span><span class="st0">'Sélectionnez une catégorie'</span><span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#required'</span> =<span class="sy0">&gt;</span> TRUE,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#ahah'</span> =<span class="sy0">&gt;</span> array<span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'path'</span> =<span class="sy0">&gt;</span> <span class="st0">'courses/js/types'</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="st0">'wrapper'</span> =<span class="sy0">&gt;</span> <span class="st0">'wrapper-types'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'method'</span> =<span class="sy0">&gt;</span> <span class="st0">'replace'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="st0">'effect'</span> =<span class="sy0">&gt;</span> <span class="st0">'fade'</span><span class="br0">&#41;</span>, &nbsp;</div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2"><span class="re1">$form</span><span class="br0">&#91;</span><span class="st0">'wrapper-types'</span><span class="br0">&#93;</span> = array<span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#prefix'</span> =<span class="sy0">&gt;</span> <span class="st0">'&lt;div id=&quot;wrapper-types&quot;&gt;'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'#suffix'</span> =<span class="sy0">&gt;</span> <span class="st0">'&lt;/div&gt;'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'type_produit'</span> =<span class="sy0">&gt;</span> courses_types_field<span class="br0">&#40;</span><span class="re1">$node</span>-<span class="sy0">&gt;</span>categorie_produit, <span class="re1">$node</span>-<span class="sy0">&gt;</span>type_produit<span class="br0">&#41;</span>,</div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span>;</div></li></ol></div></div></div>
</p>
<p>
  Simple mais nécessitant un peu d'explication. Le début de l'élément de formulaire <kbd>categorie_produit</kbd> ne change pas par rapport à ce que nous connaissions. Elle est alimentée par la fonction <kbd>courses_categories()</kbd> que nous avons défini plus haut et utilise <kbd>$node->categorie_produit</kbd> comme valeur par défaut. 
</p>
<p>
  Là où cela change, c'est justement avec l'attribut <kbd>#ahah</kbd> qui définit un comportement AJAX, pardon AHAH,  que la liste doit adopter. Le bloc AHAH n'ayant pas d'attribut <kbd>event</kbd>, va aller se connecter à l'événement par défaut, à savoir la sélection d'un élément de la liste. Nous aurions pu rajouter un <kbd>'event'=>'mousedown'</kbd> mais cela n'aurait pas grand intérêt. <kbd>wrapper</kbd> indique l'ID d'un DIV qui va recevoir les données, <kbd>method</kbd> dit que cette réception doit donner lieu à un remplacement de ce que contenait le DIV (cela pourrait être <kbd>before</kbd> ou <kbd>after</kbd>), <kbd>effect</kbd>, c'est pour faire joli, mettez <kbd>none</kbd> si vous n'aimez pas, et enfin <kbd>path</kbd> est l'URL vers laquelle le module AHAH doit émettre une requête pour recevoir ce fameux contenu à coller dans le DIV. 
</p>
<p>
  Ce fameux DIV est défini par l'élément de formulaire suivant avec comme ID, celui qui a été donné plus haut, et comme contenu notre fameux champ dynamique. Et comme il est dynamique, sa construction est placée dans une fonction que nous allons maintenant définir :
  <div class='code-container-area'><div class='code-container-caption'>courses.module - courses_types_field()</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">&nbsp; <span class="kw2">function</span> courses_types_field<span class="br0">&#40;</span><span class="re0">$categorie</span><span class="sy0">=</span><span class="kw2">null</span><span class="sy0">,</span><span class="re0">$type</span><span class="sy0">=</span><span class="kw2">null</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><a target="blank" &nbsp;href="http://www.php.net/empty"><span class="kw3">empty</span></a><span class="br0">&#40;</span><span class="re0">$categorie</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="re0">$types</span><span class="sy0">=</span>courses_types<span class="br0">&#40;</span><span class="re0">$categorie</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#125;</span> <span class="kw1">else</span> <span class="br0">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="re0">$types</span><span class="sy0">=</span><a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> </div></li>
<li class="li1"><div class="de1">&nbsp; <span class="br0">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <a target="blank" &nbsp;href="http://www.php.net/array_unshift"><span class="kw3">array_unshift</span></a><span class="br0">&#40;</span><span class="re0">$types</span><span class="sy0">,</span> t<span class="br0">&#40;</span><span class="st0">&quot;Type de produit&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="kw1">return</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="st0">'#type'</span> <span class="sy0">=&gt;</span> <span class="st0">'select'</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="st0">'#title'</span> <span class="sy0">=&gt;</span> t<span class="br0">&#40;</span><span class="st0">'Type'</span><span class="br0">&#41;</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="st0">'#options'</span> <span class="sy0">=&gt;</span> <span class="re0">$types</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="st0">'#description'</span> <span class="sy0">=&gt;</span> t<span class="br0">&#40;</span><span class="st0">'Sélectionnez un type'</span><span class="br0">&#41;</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="st0">'#default_value'</span><span class="sy0">=&gt;</span><span class="re0">$type</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="st0">'#required'</span> <span class="sy0">=&gt;</span> <span class="kw2">TRUE</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="st0">'#disabled'</span><span class="sy0">=&gt;</span>count<span class="br0">&#40;</span><span class="re0">$types</span><span class="br0">&#41;</span><span class="sy0">==</span><span class="nu0">1</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#125;</span></div></li></ol></div></div></div>
</p>
<p>
  Rien de compliqué là dedans, il s'agit juste de la récupération de la bonne liste de types en fonction de la catégorie et éventuellement de la définition d'une position par défaut si le paramètre <kbd>$type</kbd> est renseigné (cas de l'édition d'un produit). 
</p>
<p>
   Voilà, le décor est en place, passons à la partie rock'n'roll, la réponse à la requête AHAH.
</p>


	<a name='chapter_8'></a>
	<h2>Requête AHAH</h2>
	
<p>
   Comme nous l'avons vu, le module AHAH est censé lorsque l'utilisateur sélectionne un élément de la liste <kbd>categories</kbd>, émettre une requête vers <kbd>courses/js/types</kbd> de sorte à recevoir le nouvel élément de formulaire qui va aller remplacer l'ancien. Pour que Drupal sache répondre à cette requête, il faut donc déjà rajouter un nouveau menu (à placer avant le <kbd>return $items</kbd> :
  <div class='code-container-area'><div class='code-container-caption'>courses.module - courses_menu()</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">&nbsp; <span class="re0">$items</span><span class="br0">&#91;</span><span class="st0">'courses/js/types'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="br0">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'page callback'</span> <span class="sy0">=&gt;</span> <span class="st0">'courses_js_types'</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'type'</span> <span class="sy0">=&gt;</span> MENU_CALLBACK<span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="st0">'access callback'</span> <span class="sy0">=&gt;</span> <span class="st0">'node_access'</span><span class="sy0">,</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="st0">'access arguments'</span> <span class="sy0">=&gt;</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="br0">&#40;</span><span class="st0">'view'</span><span class="sy0">,</span><span class="nu0">1</span><span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">&#41;</span><span class="sy0">;</span></div></li></ol></div></div></div>
</p>
<p>
  Rien de nouveau ici, cela reprend la technique plus laborieuse que j'avais utilisée pour faire causer <a class='external' target='_blank' href='/node/1273' >jQuery avec Drupal</a>. Il nous reste donc à ajouter notre <kbd>callback</kbd> :
  <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="kw1">function</span> courses_js_types<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">//</span> Récupération de la <span class="kw2">cat</span>égorie</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re1">$categorie</span>=<span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'categorie_produit'</span><span class="br0">&#93;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">//</span> Fabrication de notre élément avec la bonen <span class="kw2">cat</span>égorie</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re1">$element</span>=courses_types_field<span class="br0">&#40;</span><span class="re1">$categorie</span><span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">//</span> Récupération de l<span class="st0">'ID unique du formulaire</span></div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;$form_build_id = $_POST['</span>form_build_id<span class="st0">'];</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;// On fabrique un faux form_state</span></div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;$form_state = array('</span>submitted<span class="st0">' =&gt; FALSE);</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;// Récupération du formulaire à partir du cache</span></div></li>
<li class="li2"><div class="de2"><span class="st0"> &nbsp;$form = form_get_cache($form_build_id, $form_state);</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;// On ajoute notre élément dynamique dans le formulaire (en fait, on remplace l'</span>ancien...<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re1">$form</span><span class="br0">&#91;</span><span class="st0">'wrapper-types'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'type_produit'</span><span class="br0">&#93;</span>=<span class="re1">$element</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">//</span> Sauvegarde <span class="kw2">du</span> formulaire dans le cache</div></li>
<li class="li1"><div class="de1">&nbsp; form_set_cache<span class="br0">&#40;</span><span class="re1">$form_build_id</span>, <span class="re1">$form</span>, <span class="re1">$form_state</span><span class="br0">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">//</span> Reconstruction <span class="kw2">du</span> formulaire</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re1">$form</span> = form_builder<span class="br0">&#40;</span><span class="re1">$_POST</span><span class="br0">&#91;</span><span class="st0">'form_id'</span><span class="br0">&#93;</span>, <span class="re1">$form</span>, <span class="re1">$form_state</span><span class="br0">&#41;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">//</span> Récupération de notre élément reconstruit</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="re1">$element</span> = <span class="re1">$form</span><span class="br0">&#91;</span><span class="st0">'wrapper-types'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'type_produit'</span><span class="br0">&#93;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">//</span> Transformation de l<span class="st0">'élément en HTML</span></div></li>
<li class="li2"><div class="de2"><span class="st0"> &nbsp;$output = drupal_render($element)</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;// On renvoie au client le formulaire sous sa forme HTML, convertie en JSON</span></div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;print drupal_to_js(array('</span>data<span class="st0">' =&gt; $output, '</span>status<span class="st0">' =&gt; true));</span></div></li>
<li class="li1"><div class="de1"><span class="st0"> &nbsp;exit();</span></div></li>
<li class="li2"><div class="de2"><span class="st0">} &nbsp;</span></div></li>
<li class="li1"><div class="de1"><span class="st0"></span</div></li></ol></div></div></div>
</p>
<p>
  Alors oui, j'en conviens, c'est un peu "sportif". L'idée est que AHAH ne fait pas un GET mais un POST du formulaire dans son état courant. Du coup, nous avons toutes les valeurs que l'utilisateur a déjà saisies, dont la catégorie, dans la variable <kbd>$_POST</kbd>. Cela nous permet déjà de construire notre élément dynamique. 
</p>
<p>
  Une valeur un peu étonnante envoyée par POST est <kbd>form_build_id</kbd>. Il s'agit de l'ID unique de l'instance du formulaire pour cet utilisateur. Et nous allons utiliser cet ID pour aller faucher dans le cache le formulaire complet tel que Drupal l'a sauvegardé avant de l'envoyer. Ensuite nous allons remplacer dans ce formulaire l'ancien élément <kbd>type_produit</kbd> par le nouveau et sauver le tout dans le cache. Alors pourquoi se compliquer la vie ainsi ? Simplement pour tromper Drupal et lui faire croire que le formulaire que nous sommes en train de modifier dans son dos est le même que celui qu'il a originellement envoyé à l'utilisateur. 
</p>
<p>
  Pour terminer, nous allons utiliser la fonction <kbd>form_builder</kbd> qui va régénérer le formulaire dans le même état que si Drupal était sur le point de l'envoyer. La seule différence est que nous allons extraire notre élément 'type_produit' de ce formulaire regénéré pour le passer à la fonction <kbd>drupal_render</kbd> qui va le transformer en code XHTML. 
</p>
<p>
  Dernière étape, l'utilisation de <kbd>drupal_to_js</kbd> qui va transformer ce code XHTML en un fragment au format 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/JSON'>
		  JSON
		  </a> que le module AHAH du client est capable de comprendre. Une fois cette dernière transformation faite, le tout est simplement envoyé au client. 
</p>
<p>
  Notez la fonction <kbd>exit()</kbd> qui arrête le traitement ici, interdisant à Drupal tout autre opération. 
</p>


	<a name='chapter_9'></a>
	<h2>Conclusion</h2>
	
<p>
  Voilà, c'est tout et ça marche très bien. Ne vous laissez pas trop effrayer par l'apparente complexité de l'approche car je l'ai développée au maximum. Il est possible de créer une ou deux fonctions génériques qui permettraient de faire la même chose en quelques lignes. Si cela continue dans cette voie, on va finir par pouvoir faire des choses aussi basiques que celles-ci avec la même simplicité que les Delphi & co d'il y a 10 ans... Enfin, je rêve un peu, avec les client Riches, il a fort à parier que tout cet acquis soit à nouveau remis en jeu...
</p>
    ]]></content>
  </entry>
  <entry>
    <title>Récupérer ses fichiers WAR</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1642" />
    <id>http://artisan.karma-lab.net/node/1642</id>
    <published>2008-09-23T01:26:41+02:00</published>
    <updated>2008-09-23T15:57:51+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="FireFox" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
  Rien de bien guerrier dans cette histoire, ni même de rapport avec Java. Ici les fichiers WAR sont ceux qui étaient générés (et qui le sont peut-être encore) par l'extension d'archivage de page de 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/Konqueror'>
		  Konqueror
		  </a>, des Web ARchives. Et il se trouve que dans ma grande paranoïa du 404, j'ai généré des centaines de ces fichiers, 341 pour être exacte...
</p>
<p>
 Aujourd'hui, je n'utilise plus Konqueror mais ce très cher FireFox qui ne tient sur mon bureau que par cette extension magique qu'est <a class='external' target='_blank' href='https://addons.mozilla.org/fr/firefox/addon/427' >ScrapBook</a> dont je ne pourrais juste plus me passer. Du coup, a fini par se poser le problème du passage de l'un à l'autre des deux mondes. Alors je ne sais pas si cela va intéresser grand monde, mais au cas où, voilà comment faire. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
  Rien de bien guerrier dans cette histoire, ni même de rapport avec Java. Ici les fichiers WAR sont ceux qui étaient générés (et qui le sont peut-être encore) par l'extension d'archivage de page de 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/Konqueror'>
		  Konqueror
		  </a>, des Web ARchives. Et il se trouve que dans ma grande paranoïa du 404, j'ai généré des centaines de ces fichiers, 341 pour être exacte...
</p>
<p>
 Aujourd'hui, je n'utilise plus Konqueror mais ce très cher FireFox qui ne tient sur mon bureau que par cette extension magique qu'est <a class='external' target='_blank' href='https://addons.mozilla.org/fr/firefox/addon/427' >ScrapBook</a> dont je ne pourrais juste plus me passer. Du coup, a fini par se poser le problème du passage de l'un à l'autre des deux mondes. Alors je ne sais pas si cela va intéresser grand monde, mais au cas où, voilà comment faire. 
</p>
<!--break-->


	<a name='chapter_5'></a>
	<h2>Lire le fichier WAR</h2>
	
<p>
   Techniquement les .war sont des archives 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/TAR'>
		  TAR
		  </a>, compressées par 
		  <a target='_blank' href='http://fr.wikipedia.org/wiki/gzip'>
		  gzip
		  </a>. En interne, l'utilitaire de Konqueror n'est pas mal fait car il rend standard n'importe quel site en générant systématiquement un <kbd>index.html</kbd> à la racine de l'archive, quel que fût le nom de fichier d'origine. Du coup, une première étape très simple à réaliser est un petit script permettant d'ouvrir ces archives sous FireFox à partir de Nautilus :
   <div class='code-container-area'><div class='code-container-caption'>~/.gnome2/nautilus-scripts/Ouvrir l&#039;archive</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="co0">#! /bin/sh</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># Création du dossier temporaire d'extraction</span></div></li>
<li class="li1"><div class="de1"><span class="kw3">cd</span> <span class="re1">$TMPDIR</span></div></li>
<li class="li2"><div class="de2"><span class="re2">working_path=</span>$<span class="br0">&#40;</span><span class="kw2">mktemp</span> -d<span class="br0">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">mkdir</span> <span class="re1">$working_path</span> </div></li>
<li class="li1"><div class="de1"><span class="kw3">cd</span> <span class="re1">$working_path</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># extraction de l'archive</span></div></li>
<li class="li2"><div class="de2"><span class="kw2">tar</span> -zxf <span class="st0">&quot;$1&quot;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># Affichage de la page dans firefox</span></div></li>
<li class="li1"><div class="de1">firefox index.html &nbsp; &nbsp;</div></li></ol></div></div></div>
</p>   
<p>
  A noter que cela aurait aussi bien pu être Epiphany ou tout autre navigateur. 
</p>


	<a name='chapter_6'></a>
	<h2>Lire l'URL du fichier WAR</h2>
	
<p>
   Maintenant le problème, déjà présent sous Konqueror, est que j'ai bien le contenu mais pas la possibilité de revenir à URL d'origine. Il s'agit là d'une de ces précieuses fonctions Scrapbookiennes, bien utile lorsque l'on construit une présentation, une formation ou un article et que l'on aime à citer ses sources.  Cela me permettrait aussi de pouvoir ré-archiver certaines pages avec Scrapbook qui n'altère que rarement le rendu dans l'opération. 
</p>
<p>
  C'est un peu en désespoir de cause que j'ai lancé un <kbd>vi</kbd> sur l'index.html dans un des dossiers temporaires créés par le script donné plus haut. Et au début du fichier, voici ce que l'on trouve :
  <div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sc3"><span class="coMULTI">&lt;!-- saved from:</span></div></li>
<li class="li1"><div class="de1"><span class="coMULTI">http://www.monde-diplomatique.fr/2002/03/PARSONS/16228 --&gt;</span></span></div></li></ol></div></div></div>
</p>
<p>
  Du coup, rien de plus simple, aidé de l'ami <kbd>awk</kbd>, de créer un petit script qui ouvrira l'URL d'origine du .war :
  <div class='code-container-area'><div class='code-container-caption'>~/.gnome2/nautilus-scripts/Ouvrir l&#039;URL de la source</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="co0">#! /bin/sh</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># extraction de l'URL</span></div></li>
<li class="li1"><div class="de1"><span class="re2">url=</span>$<span class="br0">&#40;</span><span class="kw2">tar</span> -Ozxf <span class="st0">&quot;$1&quot;</span> index.html <span class="sy0">|</span> <span class="kw2">head</span> <span class="nu0">-2</span> <span class="sy0">|</span> <span class="kw2">awk</span> <span class="st0">'/http/ { print $1 }'</span><span class="br0">&#41;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co0"># Affichage de la page dans firefox</span></div></li>
<li class="li1"><div class="de1">firefox <span class="re1">$url</span></div></li></ol></div></div></div>
</p>
<p>
  Simple et efficace. Stocké dans le dossier de scripts de Nautilus, l'action est directement disponible par click-droit sur un fichier. Ici <kbd>tar</kbd> est paramétré pour décompresser l'archive (<kbd>zxf</kbd>) mais dans la sortie standard (<kbd>O</kbd>) et seulement le fichier <kbd>index.html</kbd>. <kbd>head</kbd> récupère les deux premières lignes (<kbd>-n 2</kbd>) qui nous intéressent et <kbd>awk</kbd> recherche le motif <kbd>http</kbd> et lorsqu'il le trouve renvoie le première argument de la ligne sélectionnée. 
</p>
<p>
  Après connaissant le truc, rien n'empêche d'aller plus loin en ouvrant une série de fichiers war via la commande <kbd>find</kbd> pour les Scrapbooker en masse. 
</p>


	<a name='chapter_7'></a>
	<h2>Conclusion</h2>
	
<p>
  Voilà, sans doute encore un bon exemple de ce qu'un petit script tout bête peut apporter dans la vie de tous les jours. 
</p>
    ]]></content>
  </entry>
  <entry>
    <title>Faire des Todo-Lists avec gedit</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1625" />
    <id>http://artisan.karma-lab.net/node/1625</id>
    <published>2008-09-09T09:13:49+02:00</published>
    <updated>2008-09-09T13:42:19+02:00</updated>
    <author>
      <name>advaya</name>
    </author>
    <category term="gnome" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>Déjà pourquoi utiliser gedit plutôt que gTodo ou évolution ? Principalement pour deux raisons : la première est que n'étant pas un directeur de projet, je n'ai à traiter que des "petites" tâches du genre liste de courses, personnes à contacter dans la semaine, etc. La seconde est que gedit est en passe de devenir ma <external href='/node/1619'>plate-forme de travail</external> privilégiée m'offrant un environnement presque équivalent à <a class='external' target='_blank' href='http://kile.sourceforge.net/' >kile</a>, de faire des scripts, de travailler sur du html/php ...
</p>
<p>
Le but de ce tutoriel est donc de réaliser cela en apprenant au passage à fabriquer soi-même une coloration syntaxique adaptée.
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>Déjà pourquoi utiliser gedit plutôt que gTodo ou évolution ? Principalement pour deux raisons : la première est que n'étant pas un directeur de projet, je n'ai à traiter que des "petites" tâches du genre liste de courses, personnes à contacter dans la semaine, etc. La seconde est que gedit est en passe de devenir ma <external href='/node/1619'>plate-forme de travail</external> privilégiée m'offrant un environnement presque équivalent à <a class='external' target='_blank' href='http://kile.sourceforge.net/' >kile</a>, de faire des scripts, de travailler sur du html/php ...
</p>
<p>
Le but de ce tutoriel est donc de réaliser cela en apprenant au passage à fabriquer soi-même une coloration syntaxique adaptée.
</p>
<!--break-->



	<a name='chapter_1'></a>
	<h2>Les Todo-lists</h2>
	
<p>Je ne vais pas faire une méta-théorie sur les todo-list, j'imagine que cela existe déjà <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/>. Dans mon utilisation personnelle, je retiens en gros quatre propriétés importantes pour les tâches d'une todo-list : 

<ul><li>Le Statut : les tâches à faire (non encore en cours d'exécution) ; les tâches en cours ; les tâches terminées.</li>
<li>La Priorité : essentiellement en trois catégories : haute, moyenne ou basse ; ou encore un chiffre entre 1 et 5</li>
<li>La Réalisation : le degré d'achèvement de la tâche, que l'on peut représenter par un chiffre entre 1 et 5 ici aussi.</li>
<li>L' Ultimatum : la date à laquelle la tâche doit être accomplie entièrement.</li></ul></p>

<p> Bien entendu, on pourrait ajouter des tas d'autres propriétés comme la liste des personnes concernées par la tâche, le "coût" de celle-ci, bref une gestion des ressources etc ... mais là on rentre dans de la «vraie» gestion de projet ce qui n'est pas l'objet de ce billet.</p>

<p>En revanche, j'ajoute personnellement deux autres concepts de tâche. Le premier est la tâche mise en attente, qui dépend d'autres tâches ou d'autres informations pour rentrer dans un processus d'exécution. La seconde que j'appelle «tâche floue». Ce type de tâche n'a ni priorité, ni ultimatum, ni statut bien défini, mais elle reste présente à l'esprit en tâche de fond, un peu comme la "pensée de la semaine".</p>

<p>Le but est donc de trouver un système de représentation compatible avec gedit permettant de faire des listes de tâches incluant toutes ces caractéristiques de façon lisible. En suivant l'exemple donné dans <a class='external' target='_blank' href='http://crunchbang.org/archives/2007/09/25/gedit-todo-hack'/>ce billet</a>, on va pousser un peu plus loin la logique et utiliser deux choses : d'une part une notation adaptée aux différents status des tâches ; d'autre part une coloration syntaxique ad-hoc, correspondant à ce système de notation. </p>


	<a name='chapter_2'></a>
	<h2>Le système de notation</h2>
	
<p>Le principe de départ est que la liste se lira ligne par ligne (cliquez sur l'image ci-dessus). En début de ligne de chaque tâche sera mentionné son statut à l'aide d'un code entre parenthèses : (!) pour une tache importante ; (o) pour les tâches à prévoir ; (-) pour les tâches en cours d'exécution ; (x) pour les tâches terminées ; ( : ) pour les tâches "suspendues" ou "mises en attente" et (~) pour les tâches floues. Comme on le voit, le statut est ici mélangé avec la priorité (tâche importante ou urgente). C'est ce qui personnellement me convient, mais libre à vous d'adapter selon votre conception de la chose.</p>

<p>On va ensuite écrire sur la ligne correspondante un descriptif sommaire de la tâche, ainsi qu'éventuellement son ultimatum, précédé de "<<", et enfin un code représentant son niveau de réalisation, entre crochets. On ajoute un descriptif plus complet (optionnel) et d'autres renseignements sous cette ligne à l'aide de la barre verticale "|". L'utilisation de la touche TAB est vivement conseillée afin d'aligner les colonnes pour les différentes tâches. Dans l'exemple ci-dessous, on voit directement que la tâche qui doit être accomplie avant le 28/07, l'est pour le moment au 3/5 :</p>
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">(-) résumé de la tâche en cours d'exécution &nbsp; &lt;&lt; 28/07/09 &nbsp; [xxx--]</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; | autres renseignements et informations</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; | relative à cette tâche</div></li></ol></div></div></div>


	<a name='chapter_3'></a>
	<h2>gtksourceview</h2>
	
<p>Nous avons maintenant besoin de faire une coloration syntaxique adaptée à notre définition de todo-list.</p>
<p>Pour cela, il faut d'abord savoir que les différentes colorations syntaxiques de gedit sont définies par des fichiers de type xml contenus dans <kbd>/usr/share/gtksourceview-2.0</kbd>. On trouve d'abord dans le sous-répertoire <kbd>language-specs</kbd> des fichiers .lang, qui décrivent la coloration syntaxique pour chaque type de fichier (css, script python, html etc ...). Le fichier <kbd>css.lang</kbd>, par exemple, décrit une suite de contextes (mot-clefs, propriétés etc ...) en adjoignant à chacun un style de présentation adapté. Par exemple,</p>
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;comment&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;comment&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;/</span>\<span class="sy0">*&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>\<span class="sy0">*/&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li></ol></div></div></div>
<p>définit le contexte "comment" pour les commentaires, qui commence par <kbd>/*</kbd> et se termine par <kbd>*/</kbd>, auquel est associé le style "comment". Il faut alors remonter un peu plus haut dans le fichier pour voir la ligne suivante :</p>
<div class='code-container-area'><div class='code-container'><div class="code"><span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;comment&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Comment&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;def:comment&quot;</span><span class="sy0">/&gt;</span></div></div></div> 
<p>qui nous permet de savoir que le style "comment" pour les fichiers de type .css est en fait mappé vers "def:comment".</p>

<p>Nous voila bien avancé, mais concrètement, où est défini "def:comment" ? C'est là qu'intervient le dossier <kbd>/usr/share/gtksourceview-2.0/styles</kbd>. On y trouve quelques fichiers .xml, dont le "classic.xml" qui est celui utilisé par défaut par gedit (pour choisir d'autres palettes de couleur, allez dans préférences->police et couleurs).</p>

<p>Un listing du fichier classic.xml nous donne alors, entre autres choses : </p>
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sy0">&lt;!</span>-- Comments --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;<span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:comment&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;blue&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;<span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:shebang&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;blue&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;<span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:doc-comment-element&quot;</span> &nbsp; &nbsp; <span class="re2">italic=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li></ol></div></div></div>
<p>Nous y sommes : les commentaires entre /* et */ seront ainsi représentés en bleu (<kbd>foreground="blue"</kbd>) dans gedit si on choisit la coloration sytaxique de type css.</p>


	<a name='chapter_4'></a>
	<h2>Mise en place </h2>
	
<p>L'intérêt d'utiliser un système de notation en début de ligne pour chaque tâche est qu'il permet de déclencher une coloration syntaxique adaptée selon le statut. Idem pour la barre verticale, qui nous permettra d'assigner un style aux informations relatives à la tâche.</p>

<p>Pour cela, il va falloir d'une part créer un fichier <kbd>todo.lang</kbd> qui liste nos "contextes", et d'autre part définir concrètement les attributs de style que nous voulons voir appliqués à chaque contexte. Pour ce dont nous avons besoin, c'est assez simple, voici le fichier todo.lang que j'utilise :</p>
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sy0">&lt;</span>?xml <span class="re2">version=</span><span class="st0">&quot;1.0&quot;</span> <span class="re2">encoding=</span><span class="st0">&quot;UTF-8&quot;</span>?<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="sy0">&lt;</span>language <span class="re2">id=</span><span class="st0">&quot;todo&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;TODO&quot;</span> <span class="re2">version=</span><span class="st0">&quot;2.0&quot;</span> <span class="re2">_section=</span><span class="st0">&quot;Others&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>metadata<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>property <span class="re2">name=</span><span class="st0">&quot;mimetypes&quot;</span><span class="sy0">&gt;</span>text<span class="sy0">/</span>plain<span class="sy0">&lt;/</span>property<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="sy0">&lt;</span>property <span class="re2">name=</span><span class="st0">&quot;globs&quot;</span><span class="sy0">&gt;*</span>.todo<span class="sy0">&lt;/</span>property<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;/</span>metadata<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>styles<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;comment&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Comment&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:comment&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;important&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Important&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:important&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;todos&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Todo&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:todos&quot;</span><span class="sy0">/&gt;</span> &nbsp; &nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;pending&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Pending&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:pending&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;closed&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Closed&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:closed&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;title&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Title&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:title&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;suspend&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Suspend&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:suspend&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;descrip&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Description&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:descrip&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;complement&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Complement&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:complement&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;realisation&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Realisation&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:realisation&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;before&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Before&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:before&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="sy0">&lt;</span>style <span class="re2">id=</span><span class="st0">&quot;fuzzy&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;Fuzzy&quot;</span> map-<span class="re2">to=</span><span class="st0">&quot;todo:fuzzy&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;/</span>styles<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>definitions<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp;<span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;todo&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="sy0">&lt;</span>include<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;fuzzy&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;fuzzy&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>~\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>\.<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;descrip&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;descrip&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="sy0">*&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;comment&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;comment&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="co0">#&lt;/start&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;complement&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;complement&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="sy0">|&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;important&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;important&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>\<span class="sy0">!</span>\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;pending&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;pending&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>\-\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>include<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;realisation&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;realisation&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#91;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>\<span class="br0">&#93;</span><span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;/</span>include<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;todos&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;todos&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>o\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;closed&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;closed&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>x\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;suspend&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;suspend&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#40;</span>\:\<span class="br0">&#41;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>$<span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;</span>context <span class="re2">id=</span><span class="st0">&quot;title&quot;</span> style-<span class="re2">ref=</span><span class="st0">&quot;title&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>start<span class="sy0">&gt;</span>\<span class="br0">&#91;</span><span class="sy0">&lt;/</span>start<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="sy0">&lt;</span>end<span class="sy0">&gt;</span>\<span class="br0">&#93;</span><span class="sy0">&lt;/</span>end<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="sy0">&lt;/</span>include<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="sy0">&lt;/</span>context<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;/</span>definitions<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="sy0">&lt;/</span>language<span class="sy0">&gt;</span></div></li></ol></div></div></div>
<p>On voit donc au début du fichier une série de contextes : fuzzy, descrip, comment, etc ... auquel on associe les styles todo:fuzzy, todo:descrip ... (c'est plutôt explicite). Concrètement, ces styles seront définis dans le fichier .xml que nous allons créer ensuite. Mais revenons à notre todo.lang : on a un contexte général identifié par l'id "todo", puis la balise include permet de mettre des sous-contextes à l'intérieur. Ca commence par le contexte "fuzzy" qui débute par (~) (attention aux caractères spéciaux) et se finit en bout de ligne (c'est le $ qui le précise). En pratique, dès qu'une ligne commencera par (~), on lui appliquera le style "todo:fuzzy".</p>

<p>Le reste du code est similaire, on définit pour chaque statut un style qui sera appliqué à toute la ligne. Il faut juste préciser le cas particulier du contexte "realisation" qui est lui-même inclus dans le contexte "pending", puisque le degré de réalisation de la tâche sera affiché sur la même ligne, mais avec un style différent. Sauvez le fichier sous le nom todo.lang dans le dossier language-specs.</p>

<p>Venons-en maintenant à notre fichier .xml : en gros il suffit de reprendre le classic.xml et de lui adjoindre quelques définitions de style supplémentaires. Voici le fichier ClassicwithTodo.xml que j'utilise, seules quelques définitions de couleur et la dernière section (todo-list) sont ajoutées. Je crois que le code est suffisament explicite.</p>
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="sy0">&lt;</span>?xml <span class="re2">version=</span><span class="st0">&quot;1.0&quot;</span> <span class="re2">encoding=</span><span class="st0">&quot;UTF-8&quot;</span>?<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="sy0">&lt;</span>style-scheme <span class="re2">id=</span><span class="st0">&quot;todo&quot;</span> <span class="re2">_name=</span><span class="st0">&quot;ClassicTodo&quot;</span> <span class="re2">version=</span><span class="st0">&quot;1.0&quot;</span><span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>author<span class="sy0">&gt;</span>Advaya<span class="sy0">&lt;/</span>author<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>_description<span class="sy0">&gt;</span>Classic with todo<span class="sy0">&lt;/</span>_description<span class="sy0">&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Palette --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;blue&quot;</span> &nbsp; &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#0000FF&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;magenta&quot;</span> &nbsp; &nbsp;<span class="re2">value=</span><span class="st0">&quot;#FF00FF&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;violet&quot;</span> &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#6A5ACD&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;cyan&quot;</span> &nbsp; &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#008A8C&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;green&quot;</span> &nbsp; &nbsp; &nbsp;<span class="re2">value=</span><span class="st0">&quot;#2E8B57&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;bordeaux&quot;</span> &nbsp; <span class="re2">value=</span><span class="st0">&quot;#A52A2A&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;red&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">value=</span><span class="st0">&quot;#FF0000&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;yellow&quot;</span> &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#FFFF00&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;purple&quot;</span> &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#A020F0&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;grey&quot;</span> &nbsp; &nbsp; &nbsp; <span class="re2">value=</span><span class="st0">&quot;#777777&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;darkgreen&quot;</span> &nbsp;<span class="re2">value=</span><span class="st0">&quot;#0C6836&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>color <span class="re2">name=</span><span class="st0">&quot;mauve&quot;</span> &nbsp; &nbsp; &nbsp;<span class="re2">value=</span><span class="st0">&quot;#A020F0&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Bracket Matching --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;bracket-match&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;#white&quot;</span> <span class="re2">background=</span><span class="st0">&quot;#grey&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;bracket-mismatch&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;#white&quot;</span> <span class="re2">background=</span><span class="st0">&quot;#red&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;!</span>-- Search Matching --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;search-match&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">background=</span><span class="st0">&quot;yellow&quot;</span><span class="sy0">/&gt;</span> &nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Comments --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:comment&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;blue&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:shebang&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;blue&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:doc-comment-element&quot;</span> &nbsp; &nbsp; <span class="re2">italic=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Constants --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:constant&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;magenta&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:special-char&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;violet&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Identifiers --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:identifier&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;cyan&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="sy0">&lt;!</span>-- Statements --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:statement&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">foreground=</span><span class="st0">&quot;bordeaux&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Types --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:type&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;green&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;!</span>-- Others --<span class="sy0">&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:preprocessor&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;purple&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:error&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re2">background=</span><span class="st0">&quot;red&quot;</span> <span class="re2">bold=</span><span class="st0">&quot;true&quot;</span><span class="sy0">/&gt;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="sy0">&lt;</span>style <span class="re2">name=</span><span class="st0">&quot;def:note&quot;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re2">foreground=</span><span class="st0">&quot;blue&quot;</span> <span class="re2">background=</span><span class="st0">&quot;