<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Artisan Numérique</title>
  <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net"/>
  <link rel="self" type="application/atom+xml" href="http://artisan.karma-lab.net/atom/feed"/>
  <id>http://artisan.karma-lab.net/atom/feed</id>
  <updated>2008-05-17T14:26:30+02:00</updated>
  <entry>
    <title>Des applications QT qui lookent comme GTK</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1540" />
    <id>http://artisan.karma-lab.net/node/1540</id>
    <published>2008-05-16T08:13:48+02:00</published>
    <updated>2008-05-17T14:25:23+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Gnome" />
    <category term="En Bref" />
    <category term="Planet Libre" />
    <summary type="html"><![CDATA[<p>
   Voilà un micro projet qui risque de bien arranger les choses entre les deux mondes : un plugin QtStyle qui utilise (et non pas émule) le moteur de rendu GTK2. Le résultat étant des applications QTistes visuellement (mais pas ergonomiquement) identiques à leurs sœurs GTKistes. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Voilà un micro projet qui risque de bien arranger les choses entre les deux mondes : un plugin QtStyle qui utilise (et non pas émule) le moteur de rendu GTK2. Le résultat étant des applications QTistes visuellement (mais pas ergonomiquement) identiques à leurs sœurs GTKistes. 
</p>
<!--break-->
<p>
<image id="1" width="300px"/>
   <a class='external' target='_blank' href='http://labs.trolltech.com/page/Projects/Styles/GtkStyle' >QGtKStyle</a> est donc l'équivalent du célèbre <a class='external' target='_blank' href='http://freedesktop.org/wiki/Software/gtk-qt' >GTK-QT</a> qui fait la même chose, mais dans l'autre sens. La même chose mais avec un peu plus de bonheur semblerait-il si l'on se base pour en juger sur les copies d'écran fournies par l'auteur. </p>
<p>   Ceci dit, faut pas s'emballer, ce n'est pas encore tout de suite, pour moi en tout cas, que VirtualBox ou <a class='external' target='_blank' href='http://www.framasoft.net/article3659.html' >Vym</a> vont ressembler à cela. Car pour l'instant je n'ai juste pas réussi à le compiler. En effet ce code est seulement compatible avec la version 4 de Qt, et même avec la version 4.4 pour être exact, ce qui semble être la cause de mon blocage avec Mandriva qui n'a pour l'instant que la 4.3 en réserve. 
</p>
<p>
   Encore un peu d'attente donc pour voir si le projet continue à vivre (car ce n'est pas toujours le cas chez trolltech). En tout cas, l'idée est suffisamment bonne et le rendu suffisamment prometteur pour être relevé. 
</p>    ]]></content>
  </entry>
  <entry>
    <title>Code Highlighter</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1539" />
    <id>http://artisan.karma-lab.net/node/1539</id>
    <published>2008-05-16T00:52:05+02:00</published>
    <updated>2008-05-17T14:26:09+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Drupal" />
    <category term="Aucun" />
    <category term="Projets" />
    <summary type="html"><![CDATA[<p>
   Ce module est celui utilisé sur le site pour colorier le code source. Il utilise en interne l'excellent moteur de colorisation <a class='external' target='_blank' href='http://qbnz.com/highlighter/' >GeSHI</a>. 
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Ce module est celui utilisé sur le site pour colorier le code source. Il utilise en interne l'excellent moteur de colorisation <a class='external' target='_blank' href='http://qbnz.com/highlighter/' >GeSHI</a>. 
</p>
<!--break-->
<p>
  Pour l'utiliser, il suffit d'ajouter le filtre dans l'administration drupal et de l'utiliser de la manière suivante: encadrer le code à colorer avec les balises<br>
<kbd>&lt;code type="java" caption="un titre"&gt;</kbd><br>
Votre code<br>
<kbd>&lt;/code&gt;</kbd><br>
</p>
<p>
  Je n'avais pas pensé à l'origine que ce code puisse intéresser certains. Il est donc tout vilain à l'intérieur <img src="/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" /> Dés que j'aurais le temps, j'y mettrais un grand coup de propre. 
</p>    ]]></content>
  </entry>
  <entry>
    <title>Flash 10, la nouvelle plateforme RIA libre ?</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1536" />
    <id>http://artisan.karma-lab.net/node/1536</id>
    <published>2008-05-15T14:35:06+02:00</published>
    <updated>2008-05-15T19:39:13+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Formats de fichiers" />
    <category term="HTML, JavaScript, DOM, CSS &amp; co." />
    <category term="En Bref" />
    <category term="Planet Libre" />
    <summary type="html"><![CDATA[<p>
  Une nouvelle version, de nouvelles fonctionnalités, rien de bien extraordinaire me direz-vous. Et pourtant, Flash Player 10 représente un tournant tant pour Adobe que pour la communauté du libre.
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
  Une nouvelle version, de nouvelles fonctionnalités, rien de bien extraordinaire me direz-vous. Et pourtant, Flash Player 10 représente un tournant tant pour Adobe que pour la communauté du libre.
</p>
<!--break-->
<p>
  En effet, à l'heure où les plateforme de Applications Internet Riche (RIA) se multiplient comme des petits pains (PRISM, AIR, SilverLight, JavaFX, etc.), Adobe s'est donné les moyens de rendre son lecteur le plus universel possible. C'est du coup la première fois qu'un version du lecteur sort <b>en même temps</b> pour MacOS, Windows ET Linux. Ce fait en lui-même est un événement qui prouve que la plateforme libre devient de moins en moins contournable. 
</p>
<p>
   Autre aspect majeur, cette version 10 est en réalité la première d'une nouvelle génération suivant les spécification de l'<a class='external' target='_blank' href='http://www.adobe.com/aboutadobe/pressroom/pressreleases/200804/050108AdobeOSP.html' >Open Screen Project</a>.</p>
<p>
   Ce projet vise à définir une plateforme ouverte d'Application Internet Riche basée sur les formats d'Adobe. Il est soutenu entre autre par Motorola, Intel, LG Electronics et peut être résumé par les points suivants :
<ul>
<li>Ouverture des spécifications pour les formats SWF et FLV</li>
<li>Levée des restrictions d'usage sur ces formats</li>
<li>Ouverture des protocoles Apple Flash Cast et AMF</li>
<li>Suppression des coûts de licence associé au lecteur Flash et AIR.</li>
</ul>
</p>
<p>
Il n'y a donc pas à ma connaissance d'ouverture des sources des lecteur eux-mêmes mais malgré cela, cette initiative devrait rapidement être profitable à 
        <a target='_blank' href='http://fr.wikipedia.org/wiki/Special:Search?search=gnash'>
        gnash
        </a>, le lecteur libre, qui pour l'heure est d'une stabilité plus que moyenne.
</p>

<p>
   Il ne reste maintenant plus qu'à tester ce nouveau lecteur qui au delà de ce qui vient d'être dit, affiche tout de même une liste <a class='external' target='_blank' href='http://labs.adobe.com/technologies/flashplayer10/releasenotes.html' > impressionnante de nouveautés</a>. 
</p>

    ]]></content>
  </entry>
  <entry>
    <title>Créer son propre module Drupal</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1535" />
    <id>http://artisan.karma-lab.net/node/1535</id>
    <published>2008-05-15T00:32:57+02:00</published>
    <updated>2008-05-17T19:54:36+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Drupal" />
    <category term="Aucun" />
    <category term="Tutoriels" />
    <summary type="html"><![CDATA[<p> 
Une des grandes forces de Drupal réside en son architecture à base de modules. Que ce soit pour la gestion des blogs ou celle d’un forum, chaque fonction fondamentale est en réalité un simple module interagissant avec le cœur de Drupal. Et si les modules fournis en standard ne suffisent pas, des centaines d’autres <a class='external' target='_blank' href='http://drupal.org/project/Modules' >sont disponibles</a> couvrant à peu prés tous les usages. 
</p> 
<p> 
  Mais malgré cette richesse, il arrive parfois que l’on ne trouve pas LE module « qui va bien ». Alors pourquoi ne pas le fabriquer soi-même et ainsi découvrir à quel point Drupal s'adapte facilement à des besoins spécifiques. 
</p> 
    ]]></summary>
    <content type="html"><![CDATA[<p> 
Une des grandes forces de Drupal réside en son architecture à base de modules. Que ce soit pour la gestion des blogs ou celle d’un forum, chaque fonction fondamentale est en réalité un simple module interagissant avec le cœur de Drupal. Et si les modules fournis en standard ne suffisent pas, des centaines d’autres <a class='external' target='_blank' href='http://drupal.org/project/Modules' >sont disponibles</a> couvrant à peu prés tous les usages. 
</p> 
<p> 
  Mais malgré cette richesse, il arrive parfois que l’on ne trouve pas LE module « qui va bien ». Alors pourquoi ne pas le fabriquer soi-même et ainsi découvrir à quel point Drupal s'adapte facilement à des besoins spécifiques. 
</p> 
<!--break--> 
<div class='inline-box attention'>Ce gros tuto est encore en béta test <img src="/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" /></div> 
<div class='inline-box note'> 
  L'ensemble des sources de ce tutoriel est disponible <a class='external' target='_blank' href='/node/1537' >via subversion</a>. 
</div> 
<h2>Où allons nous ?</h2> 
<p> 
  Les prérequis pour développer un module sont évidemment une bonne connaissance des concepts clés de Drupal (node, type de contenu, taxonomy, etc.), une relative maîtrise de PHP et quelques bases en SQL. Armé de tout cela, nous allons dans ce tutoriel chercher à construire un module permettant de gérer une simple liste de courses. Il s'agira en quelque sorte de la version moderne du tableau sur le réfrigérateur, amélioré d'une petite gestion des réserves. Le module permettra : 
<ul> 
<li>L'enregistrement, la modification et la suppression d'un produit. Un produit étant défini par son nom, sa quantité en réserve et la valeur seuil devant déclencher son rachat.</li> 
<li>L'affichage de la liste des produits. Chaque produit devant être racheté sera surligné. Sur chaque ligne, seront disposées deux actions, l'une permettant de le modifier, l'autre de décrémenter la quantité de produit restant d'une unité.</li> 
</ul> 
</p> 

<h2>Des modules et des hooks</h2> 
<p> 
  Avant de poursuivre, il est important de comprendre ce qu'est réellement un module. Un module Drupal est un bout de code PHP que l'on peut activer ou désactiver, et qui implémente des "hooks".  
</p> 
<p> 
  Un hook (en français, "Crochet") est un prototype de fonction dédié à une tâche spécifique, comme par exemple mettre en forme le contenu d'un node avant affichage. Chaque module peut ainsi implémenter ce hook sous la forme d'une fonction ayant des paramètres calqués sur ceux du prototype. Dans le cas de la mise en forme, le prototype se nomme <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_view/5' >hook_view</a></kdb>: 
<div class='code-container-area'><div class='code-container-caption'>prototype du hook &#039;hook_view&#039;</div><div class='code-container'><div class="code"><span class="keyword">function</span> hook_view<span class="brackets">&#40;</span><span class="0">$node</span>, <span class="0">$teaser</span> = <span class="keyword">FALSE</span>, <span class="0">$page</span> = <span class="keyword">FALSE</span><span class="brackets">&#41;</span></div></div></div> 
</p> 
<p> 
   Ainsi un module désirant implémenter ce hook, devra comporter le code suivant : 
  <div class='code-container-area'><div class='code-container-caption'>implémentation de &#039;hook_view&#039; dans le module &#039;monModule&#039;</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"> &nbsp; <span class="keyword">function</span> monModule _view<span class="brackets">&#40;</span><span class="0">$node</span>, <span class="0">$teaser</span> = <span class="keyword">FALSE</span>, <span class="0">$page</span> = <span class="keyword">FALSE</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span> </div></li>
<li class="li1"><div class="de1"><span class="comment">// Traitement à effectuer sur le module </span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp;<span class="brackets">&#125;</span> </div></li></ol></div></div></div> 
</p> 
<p> 
   Lorsque Drupal aura besoin de mettre en forme un node, il cherchera dans la liste de toutes les fonctions PHP disponibles, c'est-à-dire les fonctions contenues dans tous les modules activés, thèmes compris, celles dont le nom commence par le nom d'un module et se termine par <kbd>_view</kbd>, le nom du hook. 
</p> 
<p> 
   Ensuite, Drupal va appeler les fonctions les unes après les autres et chacune d'entre elles va modifier l'objet <kbd>$node</kbd> passé en paramètre. 
</p> 
<p> 
   Le hook <kbd>hook_view</kbd> est défini et utilisé par le module <kbd>modules/node.module</kbd> fournis en standard. Nous comprenons ainsi que tous les modules peuvent proposer leur propre hook pour permettre à d'autres modules de prendre en charge un traitement spécifique. Pour avoir la liste des hooks fournis par un module, il faut se reporter à sa documentation. Dans le cas spécifique des modules fournis en standard dans Drupal (node, book, etc), vous trouverez leur description dans la <a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_view/5' >documentation de l'API Drupal</a>. Bien l'étudier est important car chaque hook a son propre fonctionnement. Certains s'attendent à ce que le contenu de l'objet passé en paramètre soit modifié, d'autres qu'un tableau soit renvoyé par la fonction, etc. 
</p> 

<h2>Structure d'un module</h2> 
<p> 
Un module est donc un fournisseur d'implémentations de hooks. En conséquence, la majeure partie de son code va s'insérer dans les traitements de Drupal et en étendre le comportement. Maintenant de manière plus prosaïque, un module est simplement un dossier qui contient au minimum deux fichiers. 
</p> 
<p> 
Ce dossier doit porter le nom du module et doit être placé dans l'arborescence de Drupal, à un endroit qui soit approprié pour que ce dernier puisse le voir. Techniquement il est possible de les mettre dans <kbd>modules/</kbd> mais vous seriez vite embêtés lors d'un changement de version. Il est donc préconisé de mettre cela dans un dossier <kbd>sites/all/modules/mes_modules</kbd> que vous créerez si nécessaire. Ainsi lorsque vous changez de version, il suffit de déplacer le dossier <kbd>sites</kbD> à sa place dans la nouvelle arborescence. 
</p> 
<p> 
	Dans ce dossier <kbd>mes_modules</kbd>, vous allez donc créer le sous-dossier du module lui-même, dans notre cas <kbd>courses</kbd>. A l'intérieur de celui-ci, vous devez avoir au minimum deux fichiers, portant le même nom de base que le dossier parent : <kbd>courses.info</kbd> et <kbd>courses.module</kbd>. Ce qui nous donne l'arborescence suivante : 
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">&nbsp; modules </div></li>
<li class="li1"><div class="de1">&nbsp; ... etc ... </div></li>
<li class="li1"><div class="de1">&nbsp; site </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; all </div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; modules </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; contributions </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; ...etc... </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; mes_modules </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; courses </div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; courses.info </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; courses.module </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ...etc... </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; themes </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; ...etc... </div></li></ol></div></div></div> 
</p> 

<p> 
  Le fichier <kbd>courses.info</kbd> est un simple fichier texte contenant des informations sur le module comme son nom, sa description, la version de Drupal avec lequel il est compatible, etc. Ce qui nous donne pour notre exemple, le contenu suivant : 
<div class='code-container-area'><div class='code-container-caption'>courses/courses.info</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">name = &quot;Liste des courses&quot; </div></li>
<li class="li1"><div class="de1">description = &quot;Gestion de la liste des courses&quot; </div></li>
<li class="li1"><div class="de1">package = karma-lab </div></li>
<li class="li1"><div class="de1">version = &quot;5.x-1.0&quot; </div></li>
<li class="li2"><div class="de2">project = &quot;courses&quot; </div></li></ol></div></div></div> 
</p> 
<p> 
  Le second fichier est le code PHP du module. C'est lui qui va héberger les hooks que nous souhaitons implémenter. Pour l'instant, nous allons faire simple et n'en mettre aucun. Notre fichier va donc contenir une simple balise de démarrage de code PHP : 
<div class='code-container-area'><div class='code-container-caption'>courses/courses.module</div><div class='code-container'><div class="code"><span class="keyword">&lt;?php</span></div></div></div> 
</p> 
<p> 
<image id="1" width="300px"/> 
  Voilà, nous avons maintenant un module aussi simple qu'inutile, que Drupal est cependant capable de voir et d'activer. Pour s'en convaincre, il suffit d'aller faire un tour en <kbd>http://mon_site_drupal/?q=/admin/build/modules</kbd> pour le voir, comme sur l'illustration, apparaître dans la liste. Vous constatez que les informations affichées sont les champs <kbd>name</kbd> et <kbd> description</kbd> de notre fichier <kbd>courses.info</kbd>. 
</p> 
<p> 
  A ce stade nous pourrions déjà activer notre module, mais ne le faites pas tout de suite, nous avons encore quelque chose à rajouter. </p> 
<h2>Base de données</h2> 
<p> 
  Notre module <kbd>courses</kbd> a pour finalité de pouvoir saisir des produits et de les présenter sous la forme d'une liste. Nous pourrions pour cela utiliser un type de contenu simple comme <kbd>story</kbd> mais cette structure de stockage serait trop limitée car nous avons des informations plus précises à saisir que les seuls titre et corps. Il nous faut en effet spécifier le nombre de produits restants et le nombre minimum à avoir en réserve pour pouvoir générer notre liste de courses. Nous allons donc créer un nouveau type de node, <kbd>produit</kbd>, en cherchant à étendre le type de node simple. 
 </p> 
<p> 
   Drupal stocke les données de base d'un node (titre, corps, etc.) dans la table <kbd>node</kbd> dont la clef primaire est le champ <kbd>nid</kbd>. Ce sont les valeurs de ce champ que vous retrouvez dans l'URL d'un node, sous la forme <kbd>http://mon_site_drupal?q=/node/1234</kbd>. Elle dispose aussi d'un champ <kbd>type</kbd> de type texte identifie le type du contenu. Dans notre cas ce type prendra la valeur <kbd>produit</kbd>. 
</p> 
<p> 
  Mais pour stocker nos informations supplémentaires, nous allons devoir créer une nouvelle table, traditionnellement nommée <kbd>node_produit</kbd>, contenant un champ <kbd>quantité</kbd> pour la quantité restante du produit et un champ <kbd>seuil</kbd> pour la valeur minimum à conserver en réserve. En outre elle disposera, elle aussi, d'un champ <kbd>nid</kbd>. Les informations sur un produit donné seront donc réparties sur les champs des deux tables à la fois (<kbd>node</kbd> et <kbd>node_produit</kbd>)reliées entre-elles par le même <kbd>nid</kbd>. 
</p> 
<p> 
 Du coup, pour obtenir toutes les informations disponibles sur un node de type <kbd>produit</kbd>, nous pouvons formuler la requête suivante : 
<div class='code-container-area'><div class='code-container'><div class="code"><span class="keyword">SELECT</span> n.*,i.* <span class="keyword">FROM</span> node n, node_produit i <span class="keyword">WHERE</span> n.nid=i.nid <span class="keyword">AND</span> n.type=<span class="string">'produit'</span>;</div></div></div> 
</p> 
<p> 
   Alors nous pourrions évidemment créer cette nouvelle table à la main mais ce serait bien peu pratique lorsque viendra le moment de redistribuer ce module. La bonne pratique est donc de déléguer ce travail à Drupal lors de l'activation du module. De la même manière, nous allons lui indiquer comment la supprimer, lorsque le module est désactivé <b>puis</b> désinstallé. 
</p> 
<p> 
   Pour ce faire, il nous faut rajouter un troisième fichier à notre dossier module : <kbd>courses.install</kbd> qui va contenir deux hooks. Le premier, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_install/5' >hook_install</a></kbd>, est invoqué lors de la première activation du module (installation). Le second, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_uninstall/5' >hook_uninstall</a></kbd>, est quant à lui invoqué lors de la désinstallation du module. 
<div class='code-container-area'><div class='code-container-caption'>courses/courses.install</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">&lt;?php</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="multi-line-comment">/**</span></div></li>
<li class="li1"><div class="de1"><span class="multi-line-comment">&nbsp;* Implementation of hook_install().</span></div></li>
<li class="li2"><div class="de2"><span class="multi-line-comment">&nbsp;*/</span></div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_install<span class="brackets">&#40;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$result</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">switch</span> <span class="brackets">&#40;</span><span class="0">$GLOBALS</span><span class="brackets">&#91;</span><span class="string">'db_type'</span><span class="brackets">&#93;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="comment">// Installation de la table pour MySQL</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="keyword">case</span> <span class="string">'mysql'</span> :</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="keyword">case</span> <span class="string">'mysqli'</span> :</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="0">$result</span><span class="brackets">&#91;</span><span class="brackets">&#93;</span>= db_query<span class="brackets">&#40;</span><span class="string">&quot;</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp;CREATE TABLE {node_produit} (</span></div></li>
<li class="li1"><div class="de1"><span class="string">&nbsp; &nbsp; nid int(10),</span></div></li>
<li class="li2"><div class="de2"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;quantite int(10),</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;seuil int(10),</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;PRIMARY KEY (nid)</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;)&quot;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="keyword">break</span>;</div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="comment">// Installation de la table pour PostgreSQL</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="keyword">case</span> <span class="string">'pgsql'</span> :</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <a target="blank" &nbsp;href="http://www.php.net/error_log"><span class="keyword">error_log</span></a><span class="brackets">&#40;</span><span class="string">&quot;install on Psql&quot;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="0">$result</span><span class="brackets">&#91;</span><span class="brackets">&#93;</span>=db_query<span class="brackets">&#40;</span><span class="string">&quot;</span></div></li>
<li class="li2"><div class="de2"><span class="string">&nbsp; &nbsp; CREATE TABLE {node_produit} (</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; nid INTEGER,</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; quantite INTEGER,</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; seuil INTEGER,</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; PRIMARY KEY (nid)</span></div></li>
<li class="li2"><div class="de2"><span class="string"> &nbsp; &nbsp; &nbsp; &nbsp; );&quot;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="keyword">break</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$result</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="multi-line-comment">/**</span></div></li>
<li class="li1"><div class="de1"><span class="multi-line-comment">&nbsp;* Implementation of hook_uninstall().</span></div></li>
<li class="li1"><div class="de1"><span class="multi-line-comment">&nbsp;*/</span></div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_uninstall<span class="brackets">&#40;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="0">$result</span>=<a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$result</span><span class="brackets">&#91;</span><span class="brackets">&#93;</span>=db_query<span class="brackets">&#40;</span><span class="string">'DROP TABLE {node_produit}'</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$result</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p>
	Le code n'a rien de compliqué mais quelques points cependant méritent éclaircissement.  
</p>
<p> 
  Tout d'abord, nous distinguons deux cas qui correspondent aux deux bases de données prises en charge par Drupal : mySQL et PostgreSQL. En effet, si les bases de données sont à peu près équivalentes s'agissant des requêtes INSERT,DELETE,UPDATE et SELECT, c'est un peu moins le cas pour les commandes CREATE et ALTER. 
</p> 
<p> 
  Ensuite concernant l'exécution des requêtes SQL, nous devons à tout prix éviter d'utiliser les fonctions base de données de PHP au profit de celles fournies par Drupal. En effet ces dernières présentent l'avantage de faire abstraction de la base de données sous-jacente.
</p>
<p>
 Dans le cas spécifique de l'installation et de la désinstallation, nous utilisons <kbd>update_sql</kbd> mais pour le reste du code elle sera remplacée par <kbd>db_query</kbd>. Ces deux fonctions sont pratiquement identiques à la différence près qu'<kbd>update_sql</kbD> fournit plus d'informations sur le déroulement de la requête.  Informations qui seront utilisées par l'assistant d'installation et de mise à jour de Drupal. 
</p> 
<p> 
  Dernier point : les accolades utilisées pour encadrer le nom des tables. Elles ne sont pas nécessaires mais permettent à Drupal certaines opérations de préparation des requêtes. C'est donc juste une bonne habitude à prendre. 
</p> 
<p> 
  Maintenant que notre installation est finalisée, il ne nous reste plus qu'à activer notre module dans le panneau d'administration de Drupal : <kbd>http://mon_site_drupal?q=/admin/build/modules</kbd>. Une fois cette opération achevée, vous pouvez vérifier à l'aide de votre explorateur de base de données préféré que la table <kbd>node_produit</kbd> a bel et bien été créée. Si vous désactivez et réactivez votre module, notez que le code d'installation n'est pas ré-exécuté. En effet, il vous faut d'abord désinstaller le module, pour que le hook <kbd>courses_uninstall</kbd> soit appelé et détruise la table. Ensuite, si vous réactivez à nouveau le module, la table sera recréée. 
</p> 

<h2>Définition du nouveau type de contenu</h2> 
<p> 
  Notre base est prête, notre module activé et pourtant si nous ouvrons  l'URL <kbd>http://mon_site_drupal/?q=node/add</kbd>, notre <kbd>produit</kbd> n'apparaît pas. En effet, il faut implémenter deux hooks dans le fichier <kbd>courses.module</kbd> pour indiquer à Drupal qu'un nouveau type de node est à prendre en charge. Le premier hook, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_node_info/5' >hook_node_info</a></kbd>, va décrire le nouveau type de node : 
<div class='code-container-area'><div class='code-container-caption'>à ajouter au fichier courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_node_info<span class="brackets">&#40;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'produit'</span> =&gt; <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'name'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">'Un produit'</span><span class="brackets">&#41;</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'description'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">&quot;Un produit dans la liste des courses.&quot;</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'module'</span> =&gt; <span class="string">'courses'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p> 
  Ce hook renvoie le tableau associatif de types de contenu et de leur description. Mais ici nous n'en renvoyons qu'un seul, <kbd>produit</kbd>, avec sa description et son nom usuel. L'identifiant <kbd>produit</kbd> sera utilisé dans la colonne <kbd>type</kbd> de la table <kbd>node</kbd> comme vu au chapitre précédent. Notez enfin l'utilisation de la fonction <kbd>t(...)</kbd> qui permet à Drupal de prendre en charge les éventuelles traductions de la phrase en paramètre. Là aussi, utiliser systématique cette fonction pour tous les textes est une bonne habitude à prendre. 
</p>
<p>
   Enfin, ke dernier paramètre est le nom du module qui va gérer ce type de contenu, en l'occurrence, notre module <kbd>courses<.kbd>. 
</p> 

<h2>Un formulaire spécifique</h2> 
<p> 
   Une dernière étape est nécessaire pour pouvoir disposer de notre nouveau type de contenu : mettre en place le formulaire de saisie. Mais avant cela, il est important de bien comprendre comment Drupal fait vivre un node à travers l'objet <kbd>$node</kbd>. 
</p> 
<p> 
  En effet, nombre de hooks prennent en paramètre un objet <kbd>$node</kbd>. Cet objet représente comme on peut se l'imaginer, le node en cours de traitement par Drupal. Au fur et à mesure de l'avancement de ce traitement, les différents hooks enrichissent l'objet <kbd>$node</kbd> jusqu'à y ajouter le code HTML qui sera finalement inséré dans la page. C'est ainsi que dans le template <kbd>node.tpl.php</kbd> l'affichage du titre passe par une référence à <kbd>$node->title</kbd>. 
</p> 
<p> 
  Il existe une relation étroite entre l'objet <kbd>$node</kbd> et sa représentation en base de donnée. Si le node existe déjà, Drupal initialise l'objet <kbd>$node</kbd> en remontant la ligne correspondant au <kbd>nid</kbd> recherché. L'objet reçoit ainsi tous les champs de la table node sous la forme d'attributs dont l'intitulé est le nom de la colonne et la valeur, celle qui se trouve en base. Lorsque le node n'existe pas encore, Drupal crée simplement un objet vide. C'est donc soit l'objet vide, soit l'objet chargé des valeurs de la base, que nous allons récupérer dans le hook qui va créer notre formulaire : <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_form/5' >hook_form</a></kbd>.
</p> 
<p> 
   Ce hook est chargé de transférer les valeurs du node passé en paramètre aux champs d'un formulaire qui sera affiché sur le navigateur client. Ce formulaire sera utilisé pour un <kbd>$node</kbd> vide si l'on cherche à créer un nouveau node, mais aussi sur un node "chargé" dans le cas contraire. 
<div class='code-container-area'><div class='code-container-caption'>Corps de la fonction courses_form</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_form<span class="brackets">&#40;</span>&amp; <span class="0">$node</span><span class="brackets">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$form</span><span class="brackets">&#91;</span><span class="string">'title'</span><span class="brackets">&#93;</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#type'</span> =&gt; <span class="string">'textfield'</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'#title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">&quot;Nom du produit&quot;</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#default_value'</span> =&gt; <span class="0">$node</span>-&gt;<span class="method">title</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#required'</span> =&gt; <span class="keyword">TRUE</span> </div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$form</span><span class="brackets">&#91;</span><span class="string">'body_filter'</span><span class="brackets">&#93;</span><span class="brackets">&#91;</span><span class="string">'body'</span><span class="brackets">&#93;</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'#type'</span> =&gt; <span class="string">'textarea'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">'Notes'</span><span class="brackets">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#description'</span>=&gt;t<span class="brackets">&#40;</span><span class="string">&quot;Notes sur le produit&quot;</span><span class="brackets">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#default_value'</span> =&gt; <span class="0">$node</span>-&gt;<span class="method">body</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#rows'</span> =&gt; <span class="number">10</span>, </div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'#required'</span> =&gt; <span class="keyword">FALSE</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$form</span><span class="brackets">&#91;</span><span class="string">'body_filter'</span><span class="brackets">&#93;</span><span class="brackets">&#91;</span><span class="string">'format'</span><span class="brackets">&#93;</span>= filter_form<span class="brackets">&#40;</span><span class="0">$node</span>-&gt;<span class="method">format</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$form</span><span class="brackets">&#91;</span><span class="string">'seuil'</span><span class="brackets">&#93;</span> = <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#type'</span> =&gt; <span class="string">'textfield'</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'#title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">&quot;Seuil d'achat&quot;</span><span class="brackets">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#description'</span>=&gt;t<span class="brackets">&#40;</span><span class="string">&quot;Quantité minimum en dessous de laquelle il faut racheter le produit&quot;</span><span class="brackets">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#size'</span> =&gt; <span class="number">4</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#required'</span> =&gt; <span class="keyword">TRUE</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#default_value'</span> =&gt; <span class="0">$node</span>-&gt;<span class="method">seuil</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$form</span><span class="brackets">&#91;</span><span class="string">'quantite'</span><span class="brackets">&#93;</span> = <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">&quot;Quantité&quot;</span><span class="brackets">&#41;</span>, </div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'#description'</span>=&gt;t<span class="brackets">&#40;</span><span class="string">&quot;Quantité de produit restant en réserve&quot;</span><span class="brackets">&#41;</span>, </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#type'</span> =&gt; <span class="string">'textfield'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#size'</span> =&gt; <span class="number">4</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#required'</span> =&gt; <span class="keyword">TRUE</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'#default_value'</span> =&gt; <span class="0">$node</span>-&gt;<span class="method">quantite</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$form</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p> 
	Un formulaire Drupal est en pratique un simple tableau associatif. Chaque entrée de ce tableau correspond à un champ du formulaire. Chaque champ est lui-même un tableau associant une propriété du champ à sa valeur. Une liste des types de champs et propriétés associées est disponible dans la <a class='external' target='_blank' href='http://api.drupal.org/api/file/developer/topics/forms_api_reference.html/5' >documentation de Drupal</a>. 
</p> 
<p> 
   La propriété vraiment intéressante ici est <kbd>#default_value</kbd>, car c'est elle qui va permettre d'extraire de <kbd>$node</kbd>, les valeurs de champs que le formulaire doit permettre de modifier. Lorsque l'utilisateur validera le formulaire, Drupal se chargera de réintégrer automatiquement les valeurs saisies dans l'objet <kbd>$node</kbd>. C'est pour cela que l'identifiant du champ est le même que celui de l'attribut correspondant dans <kbd>$node</kbd> (<kbd>$form['quantite']</kbd> et <kbd>$node->quantite</kbd>). 
</p> 
<p>
   Comme nous l'avons vu plus haut, l'objet <kbd>$node</kbd> est vide à sa création. Si l'on utilise donc notre formulaire tel quel, les champs <kbd>quantité</kbd> et <kbd>seuil</kbd> vont l'être aussi. Il est donc nécessaire d'implémenter un nouveau hook, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_prepare/5' >hook_prepare</a></kbd>,  qui comme son nom l'indique, a pour rôle de préparer l'objet <kbd>$node</kbd>avant utilisation dans le formulaire. 
<div class='code-container-area'><div class='code-container-caption'>à ajouter au fichier courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_prepare<span class="brackets">&#40;</span>&amp; <span class="0">$node</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$node</span>-&gt;<span class="method">quantite</span>=<span class="0">$node</span>-&gt;<span class="method">quantite</span>?<span class="0">$node</span>-&gt;<span class="method">quantite</span>:<span class="number">0</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$node</span>-&gt;<span class="method">seuil</span>=<span class="0">$node</span>-&gt;<span class="method">seuil</span>?<span class="0">$node</span>-&gt;<span class="method">seuil</span>:<span class="number">0</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div>
</p>
<p>
     Grâce à cette fonction, nous initialisons notre objet <kbd>$node</kbd> avec les bonnes valeurs par défaut. De la même manière nous pouvons valider ce que renvoie le formulaire en implémentant cette fois <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_validate/5' >hook_validate</a></kbd>
<div class='code-container-area'><div class='code-container-caption'>à ajouter au fichier courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_validate<span class="brackets">&#40;</span><span class="0">$form_id</span>, <span class="0">$form_values</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span>!<a target="blank" &nbsp;href="http://www.php.net/is_numeric"><span class="keyword">is_numeric</span></a><span class="brackets">&#40;</span><span class="0">$form_values</span><span class="brackets">&#91;</span><span class="string">'quantite'</span><span class="brackets">&#93;</span><span class="brackets">&#41;</span> || <span class="0">$form_values</span><span class="brackets">&#91;</span><span class="string">'quantite'</span><span class="brackets">&#93;</span> &lt; <span class="number">0</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; form_set_error<span class="brackets">&#40;</span><span class="string">'quantite'</span>, t<span class="brackets">&#40;</span><span class="string">'Le champ quantité doit être une valeur numérique et positive'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span>!<a target="blank" &nbsp;href="http://www.php.net/is_numeric"><span class="keyword">is_numeric</span></a><span class="brackets">&#40;</span><span class="0">$form_values</span><span class="brackets">&#91;</span><span class="string">'seuil'</span><span class="brackets">&#93;</span><span class="brackets">&#41;</span> || <span class="0">$form_values</span><span class="brackets">&#91;</span><span class="string">'seuil'</span><span class="brackets">&#93;</span> &lt; <span class="number">0</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; form_set_error<span class="brackets">&#40;</span><span class="string">'seuil'</span>, t<span class="brackets">&#40;</span><span class="string">'Le champ seuil doit être une valeur numérique et positive'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&lt;code&gt;</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;Maintenant notre formulaire est complet. Nous pouvons l<span class="string">'essayer en utilisant l'</span>URL &lt;kbD&gt;http:<span class="comment">//mon_site_drupal?q=/node/add/produit&lt;/kbd&gt;. Comme vous le voyez créer un formulaire Drupal est loin d'être complexe. </span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&lt;h2&gt;Le lien avec la base&lt;/h2&gt; </div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">Nous avons maintenant un objet &lt;kbd&gt;<span class="0">$node</span>&lt;/kbd&gt; correctement renseigné par notre formulaire. Il ne nous reste donc plus qu<span class="string">'à coder sa sauvegarde en base de données. Pour ce faire, nous allons devoir implémenter quatre hooks, un par commande SQL.</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2"><span class="string"> Commençons par le plus simple : la lecture. Elle est prise en charge par le hook &lt;kbd&gt;&lt;external href=&quot;http://api.drupal.org/api/function/hook_load/5&quot;&gt;hook_load&lt;/external&gt;&lt;kbd&gt; : </span></div></li>
<li class="li1"><div class="de1"><span class="string">&lt;code type=&quot;php&quot; caption=&quot;à ajouter à courses.module&quot;&gt; </span></div></li>
<li class="li1"><div class="de1"><span class="string">function courses_load($node)</span></div></li>
<li class="li1"><div class="de1"><span class="string">{</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;return db_fetch_object(db_query('</span>SELECT * FROM <span class="brackets">&#123;</span>node_produit<span class="brackets">&#125;</span> WHERE nid = %d<span class="string">', $node-&gt;nid));</span></div></li>
<li class="li2"><div class="de2"><span class="string">}</span></div></li>
<li class="li1"><div class="de1"><span class="string"</span></div></li></ol></div></div></div> 
</p> 
<p> 
   Comme cela a déjà été dit, chaque hook a son propre mode opératoire. A <kbd>hook_load</kbd> est passé en paramètre un objet <kbd>$node</kbd> contenant déjà les informations extraites de la table <kbd>node</kbd>. Cela inclut donc la valeur du <kbd>nid</kbd> que nous utilisons dans notre requête pour lire en base nos valeurs supplémentaires. L'objet ainsi formé est renvoyé par l'implémentation du hook pour être agrégé par Drupal à l'objet <kbd>$node</kbd>. 
</p> 
<p> 
<kbd>db_query</kbd>, dont nous parlions plus haut, renvoie un curseur sur le résultat de la requête qu'elle vient d'exécuter. Elle autorise l'utilisation des motifs <kbd>%d</kbd> et <kbd>%s</kbd> comme la fonction PHP <a class='external' target='_blank' href='http://fr.php.net/sprintf' >sprintf</a></kbd>. Cela nous permet d'éviter d'encombrer la requête et ainsi gagner en lisibilité.</p>
<p>
 Notez enfin que <kbd>db_query</kbd> ne va pas insérer de guillemets atours des chaînes. C'est donc à vous de mettre des <kbd>'%s'</kbd>. En revanche, elle se charge de l'échappement des caractères spéciaux vous protégeant des injections de code SQL malicieux. 
</p> 
<p> 
Enfin, la fonction Drupal <kbd>db_fetch_object</kbd> va, comme son nom le suggère, lire un enregistrement enregistrement à partir du curseur renvoyé par <kbd>db_query</kbd> et transformer le résultat en un objet PHP.
</p>
<p>
Voilà qui est terminé pour le chargement des données de notre nouveau type de node. Voyons maintenant comment insérer, mettre à jour ou encore détruire nos données supplémentaires en implémentant respectivement <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_insert/5' >hook_insert</a></kbd>, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_update/5' >hook_update</a></kbd> et <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_delete/5' >hook_delete</a></kbd> : 
<div class='code-container-area'><div class='code-container-caption'>à ajouter à courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_insert<span class="brackets">&#40;</span><span class="0">$node</span><span class="brackets">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; db_query<span class="brackets">&#40;</span><span class="string">&quot;INSERT INTO {node_produit} (nid, quantite, seuil) VALUES (%d, %d, %d)&quot;</span>, <span class="0">$node</span>-&gt;<span class="method">nid</span>, <span class="0">$node</span>-&gt;<span class="method">quantite</span>, <span class="0">$node</span>-&gt;<span class="method">seuil</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_update<span class="brackets">&#40;</span><span class="0">$node</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; db_query<span class="brackets">&#40;</span><span class="string">&quot;UPDATE {node_produit} set quantite=%d, seuil=%d WHERE nid=%d&quot;</span>, <span class="0">$node</span>-&gt;<span class="method">quantite</span>, <span class="0">$node</span>-&gt;<span class="method">seuil</span>, <span class="0">$node</span>-&gt;<span class="method">nid</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_delete<span class="brackets">&#40;</span>&amp;<span class="0">$node</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; db_query<span class="brackets">&#40;</span><span class="string">'DELETE FROM {node_produit} WHERE nid = %d'</span>, <span class="0">$node</span>-&gt;<span class="method">nid</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p> 
   Nous pouvons maintenant utiliser notre nouveau type de contenu. Vous pouvez ainsi créer un nouveau <kbd>produit</kbd>, saisir ses valeurs, le sauvegarder, le modifier, et le détruire. 
</p> 

<h2>Affichage du nouveau node</h2>
<p>
La première chose que l'on constate cependant est que les informations affichées lorsque l'on vient de créer un node sont loin d'être complètes. En effet la vue sur le node ne donne aucune indication sur la quantité ou le seuil de rachat du produit. Pour changer cela, nous allons implémenter un nouveau hook, <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_view/5' >hook_view</a></kbd>, et une fonction de thème.  
<div class='code-container-area'><div class='code-container-caption'>à ajouter à courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_view<span class="brackets">&#40;</span><span class="0">$node</span>, <span class="0">$teaser</span> = <span class="keyword">FALSE</span>, <span class="0">$page</span> = <span class="keyword">FALSE</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$node</span>= node_prepare<span class="brackets">&#40;</span><span class="0">$node</span>, <span class="0">$teaser</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$page</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="0">$node</span>-&gt;<span class="method">content</span><span class="brackets">&#91;</span><span class="string">'Informations'</span><span class="brackets">&#93;</span> = <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'#value'</span> =&gt; theme<span class="brackets">&#40;</span><span class="string">'courses_view'</span>, <span class="0">$node</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'#weight'</span> =&gt; <span class="number">1</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$node</span>;</div></li>
<li class="li2"><div class="de2"><span class="brackets">&#125;</span></div></li></ol></div></div></div>
</p>
<p>
Notre hook a ici pour objectif d'ajouter des sections d'informations supplémentaires avant l'affichage réel du node. Pour cela nous préparons le node à l'affichage par la fonction <kbd>node_prepare</kbd>. Cette fonction entre autre tâche, applique les filtres d'entrée, fabriquer le <kbd>teaser</kbd> s'il n'existe pas encore. 
</p>
<p>
Une fois le node préparé, nous vérifions que le contexte dans lequel nous sommes appelé. En effet, nous n'allons ajouter nos informations supplémentaires que dans le cas où Drupal cherche à afficher le node en pleine page (<kbd>$page==TRUE</kbd>). Si c'est juste pour une liste ou un teaser, on laisse l'affichage par défaut. 
</p>
<p>
   L'ajout d'information se fait par l'insertion dans le tableau associatif <kbd>$node->content</kbd> des données HTML à afficher. Chaque entrée de ce tableau forme ainsi des sections que l'on peut ordonner grâce à la propriété <kbd>#weight</kbd>. La propriété <kbd>#value</kbd> contiendra le texte lui-même. 
</p>
<p>
Pour renseigner <kbd>#value</kbd> nous rajoutons une petite finesse : l'utilisation d'une fonction de thème <kbd>theme('courses_view', $node)</kbd>. Cela consiste à demander à Drupal d'habiller l'objet <kbd>$node</kbd> avec le thème <kbd>courses_view</kbd>. La fonction en interne va procéder de la manière suivante :
<ul>
<li>Si votre site a un thème, par exemple <kbd>mon_theme</kbd>, Drupal vérifie s'il existe une fonction qui s'appelle <kbd>mon_theme_courses_view</kbd>. Si tel est le cas, c'est elle qui sera utilisée.</li>
<li>Si votre site utilise un <kbd><a class='external' target='_blank' href='http://drupal.org/project/Theme+engines' >theme engine</a></kbd>, par exemple <kbd>phptemplate</kbd>, Drupal cherche une fonction nommée <kbd>phptemplate_courses_view</kbd>.</li>
<li>Le cas échéant, Drupal cherche une fonction nommée <kbd>theme_courses_view</kbd>. Si elle existe, c'est elle qui sera utilisée.</li>
</ul>
</p>
<p>
   Une bonne pratique est donc de toujours fournir un thème par défaut en utilisant ce dernier cas de figure. Ainsi, Drupal trouver toujours de quoi habiller notre node tout en laissant à vos utilisateurs la possibilité de changer cet habillage. Là aussi c'est une bonne pratique à garder à l'esprit.  
<div class='code-container-area'><div class='code-container-caption'>à ajouter à courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> theme_courses_view<span class="brackets">&#40;</span><span class="0">$node</span><span class="brackets">&#41;</span></div></li>
<li class="li1"><div class="de1"><span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$result</span> = <span class="string">&quot;&lt;h2&gt;Détail du produit&lt;/h2&gt;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$result</span>.= <span class="string">&quot;&lt;div class='quantite'&gt;Quantité restante :&quot;</span>.<span class="0">$node</span>-&gt;<span class="method">quantite</span>.<span class="string">&quot;&lt;/div&gt;&quot;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="0">$result</span>.= <span class="string">&quot;&lt;div class='seuil'&gt;Seuil avant rachat : &quot;</span>.<span class="0">$node</span>-&gt;<span class="method">seuil</span>.<span class="string">&quot;&lt;/div&gt;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$result</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div>
</p>
<h2>Rajouter des actions standard</h2>
<p>
Vous aurez sans doute remarqué que chaque node disposent, à la fin du texte (teaser ou page), d'une série de liens qui offrent des actions qui lui sont propres. Ces liens sont définis grâce à l'implémentation de <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_view/5' >hook_view</a></kbd>. Nous allons donc utiliser cette fonctionnalité pour ajouter à chaque node un lien vers notre future liste de courses. 
<div class='code-container-area'><div class='code-container-caption'>à ajouter à courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">. </div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_link<span class="brackets">&#40;</span><span class="0">$type</span>, <span class="0">$node</span> = <span class="keyword">NULL</span>, <span class="0">$teaser</span> = <span class="keyword">FALSE</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$links</span> = <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$type</span> == <span class="string">'node'</span> &amp;&amp; <span class="0">$node</span>-&gt;<span class="method">type</span>=<span class="string">'produit'</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="0">$links</span><span class="brackets">&#91;</span><span class="string">'liste_des_courses'</span><span class="brackets">&#93;</span> = <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a><span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'title'</span>=&gt;t<span class="brackets">&#40;</span><span class="string">'Liste des courses'</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'href'</span>=&gt; <span class="string">&quot;courses/liste&quot;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="string">'attributes'</span>=&gt;array<span class="brackets">&#40;</span><span class="string">'title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">'Afficher la liste complète des courses.'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp; <span class="keyword">return</span> <span class="0">$links</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div>
</p>
<p>
   Attention cependant, contrairement à tous les hooks que nous avons étudié jusqu'à maintenant, celui là fonctionne de manière globale. C'est à dire qu'il n'est pas appelé spécifiquement pour un node de type <kbd>produit</kbd>. Et il est même utilisé ajouter des liens à d'autres éléments de Drupal, comme les commentaires. Il faut donc vérifier avant d'ajouter un lien qu'il s'agit bien d'un node, et que le type du node est bien <kbd>produit</kbd>. Ensuite tout passe par un nouveau tableau associatif pouvant contenir autant de liens que désiré. La syntaxe est assez simple pour être comprise en première lecture.
</p>
<p>
   Maintenant que nous avons notre lien vers la liste des courses, il ne nous reste plus qu'à la mettre en oeuvre. 
</p>

<h2>Liste de courses</h2> 
<p> 
   Avant de rentrer dans le vif du sujet, arrêtons nous un peu sur la manière dont Drupal gère les URL. Ces dernières sont de la forme <kbd>http://mon_site_drupal?q=chemin1/chemin2</kbd> ou  <kbd>http://mon_site_drupal/chemin1/chemin2</kbd> selon que le mode "URL Simplifié" est actif ou non. La chaîne <kbd>chemin1/chemin2</kbd>, appelée <kbd>path</kbd>, est associée par Drupal à une fonction, appelée <kbd>callback</kbd>. Lorsqu'il est exécutée, ce <kbd>callback</kbd> est renvoyé à Drupal du code HTML qu'il va l'insérer dans le corps de la page. Cette association entre un <kbd>path</kbd> et une fonction <kbd>callback</kbd> est appelée un <kbd>menu</kbd>. 
</p> 
<p> 
  Chaque module a la possibilité de définir ses propres menus en implémentant <kbd><a class='external' target='_blank' href='http://api.drupal.org/api/function/hook_menu/5' >hook_menu</a></kbd>. C'est ce que nous allons utiliser pour afficher notre liste de courses : 
<div class='code-container-area'><div class='code-container-caption'>à ajouter à courses.module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_menu<span class="brackets">&#40;</span><span class="0">$may_cache</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$items</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$may_cache</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span> <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">else</span> <span class="brackets">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="0">$items</span><span class="brackets">&#91;</span><span class="brackets">&#93;</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'title'</span> =&gt; t<span class="brackets">&#40;</span><span class="string">&quot;Liste de courses&quot;</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'path'</span> =&gt; <span class="string">'courses/liste'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'callback'</span> =&gt; <span class="string">'courses_callback_liste'</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="string">'type'</span> =&gt; MENU_NORMAL_ITEM,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; &nbsp; <span class="string">'access'</span> =&gt; <span class="keyword">TRUE</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="0">$items</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="keyword">function</span> courses_callback_liste<span class="brackets">&#40;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span> </div></li>
<li class="li1"><div class="de1">drupal_set_title<span class="brackets">&#40;</span>t<span class="brackets">&#40;</span><span class="string">'Liste des courses'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>; </div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> <span class="string">&quot;Je suis une &lt;b&gt;liste&lt;/b&gt;&quot;</span>; </div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span> </div></li></ol></div></div></div> 
</p> 
<p> 
   Une fois le code ajouté allez à l'URL <kbd>http://mon_site_drupal/?q=courses/liste_items</kbd> et voyez le résultat. Notre texte s'affiche tranquillement au milieu de la page et le titre de la fenêtre du navigateur est bien <kbd>Liste des courses</kbd>. </p> 
<p> 
	<kbd>courses_menu</kbd> renvoie à Drupal un tableau dont chaque entrée est un tableau associatif définissant un menu. Ce menu associe comme nous l'avons vu plus haut un <kbD>path</kbD> à une fonction <kbd>callback</kbd>.  Le choix de mettre le nom du module, <kbd>courses</kbd> comme premier élément du <kbd>path</kbd> est une convention permettant d'avoir des URL de la forme <kbd>mon_module/mon_action</kbd>. </p>
 A ce menu nous avons donné un titre (paramètre <kbd>title</kbd>) et vous aurez sans doute constaté qu'un lien avec ce texte est apparu en même temps que la liste dans votre bloc de navigation. Cette magie là, nous la devons au type de menu (paramètre <kbd>type</kbd>) positionné à <kbd>MENU_NORMAL_ITEM</kbd>. Cela indique à Drupal que ce doit être un menu visible.
</p>
<p>
Enfin Le dernier paramètre <kbd>access</kbd> positionné à <kbd>TRUE</kbd> permet à n'importe quel utilisateur, anonyme ou pas, d'accéder à ce menu. 
</p> 
<p> 
  La fonction de callback commence par changer le titre de la page et poursuit par la construction du contenu HTML du corps qui sera renvoyé à Drupal. 
<p> 
<p> 
  Il ne nous reste maintenant plus qu'à mettre un véritable corps à notre callback pour obtenir enfin notre liste de courses 
<div class='code-container-area'><div class='code-container-caption'>corps de la fonction de callback</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_callback_liste<span class="brackets">&#40;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$path</span>= drupal_get_path<span class="brackets">&#40;</span><span class="string">'module'</span>, <span class="string">'course'</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; drupal_add_css<span class="brackets">&#40;</span><span class="0">$path</span> . <span class="string">'/default.css'</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; drupal_set_title<span class="brackets">&#40;</span>t<span class="brackets">&#40;</span><span class="string">'Liste des Courses'</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li2"><div class="de2"><span class="0">$cursor</span> = db_query<span class="brackets">&#40;</span><span class="string">&quot;</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&nbsp; select n.*,i.* from node n </span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&nbsp; &nbsp; inner join node_produit i on n.nid=i.nid</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&nbsp; where n.type='produit'</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&nbsp; order by n.title&quot;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$output</span>= <span class="string">&quot;&lt;table class='liste_produits'&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&lt;tr&gt;&lt;th&gt;Produit&lt;/th&gt;&lt;th&gt;Seuil&lt;/th&gt;&lt;th&gt;QuantitÃ©&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="string"> &nbsp;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">while</span> <span class="brackets">&#40;</span><span class="0">$node</span>=db_fetch_object<span class="brackets">&#40;</span><span class="0">$cursor</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot;&lt;tr&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$node</span>-&gt;<span class="method">restant</span> &lt; <span class="0">$node</span>-&gt;<span class="method">minimum</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot; class='manquant'&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot;&gt;&lt;td&gt;&quot;</span>.<span class="0">$node</span>-&gt;<span class="method">title</span>.<span class="string">&quot;&lt;/td&gt;&lt;td&gt;&quot;</span>.<span class="0">$node</span>-&gt;<span class="method">seuil</span>.<span class="string">&quot;&lt;/td&gt;&lt;td&gt;$node-&gt;quantite&lt;/td&gt;&quot;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot;&lt;td&gt;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$node</span>-&gt;<span class="method">quantite</span>&gt;<span class="number">0</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="0">$output</span> .= l<span class="brackets">&#40;</span><span class="string">&quot;Diminuer&quot;</span>,<span class="string">&quot;courses/diminuer/&quot;</span>.<span class="0">$node</span>-&gt;<span class="method">nid</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot; | &quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="0">$output</span> .= l<span class="brackets">&#40;</span><span class="string">&quot;Modifier&quot;</span>,<span class="string">&quot;node/&quot;</span>.<span class="0">$node</span>-&gt;<span class="method">nid</span>.<span class="string">&quot;/edit&quot;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot;&lt;/td&gt;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="0">$output</span> .= <span class="string">&quot;&lt;/tr&gt;&quot;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$output</span>.=<span class="string">&quot;&lt;/table&gt;&quot;</span>;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="keyword">return</span> <span class="0">$output</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 


<p> 
   La fonction commence par deux lignes dont l'objectif est de demander à Drupal de prendre la feuille de style <kbd>default.css</kbd> qui se trouve dans le dossier du module, et de l'ajouter à la page finale. Pour plus d'informations sur cette méthode, reportez vous à ce <a class='external' target='_blank' href='/node/1277%2523module' >tutoriel</a>. Cette feuille de style va juste nous servir à mettre en évidence les lignes de produits qui doivent être rachetés. 
<div class='code-container-area'><div class='code-container-caption'>Fichier default.css à mettre dans le dossier du module</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">TR<span class="1">.manquant</span> TD <span class="brackets">&#123;</span> </div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">color</span>: <span class="keyword">red</span>; </div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span> </div></li></ol></div></div></div> 
</p> 
<p> 
  La requête SQL qui suit nous permet d'itérer sur la liste des nodes de type <kbd>produit</kbd> en y ajoutant les informations de la table <kbd>node_produit</kbd>. Nous aurions pu seulement récupérer ici le <kbD>nid</kbd> et utiliser la fonction <kbd>node_load</kbd> mais cette méthode est beaucoup plus rapide. Ceci fait, le reste n'est plus qu'un travail de mise en forme du contenu de l'objet <kbd>$node</kbd> en ligne d'un tableau, qui sert de retour à la fonction de call-back. 
</p> 
<p>
  La dernière colonne du tableau contient des liens qui représentent les actions par produit : modifier et diminuer. La première est simplement un raccourcis sur l'édition du node, la seconde va nous permettre de diminuer la quantité restant de produit d'un simple clique. 
</p>
<p> 
   Notez l'utilisation de la fonction Drupal, <kbd>l(…)</kbd>. Son rôle est de fabriquer un lien HTML à partir d'un texte et d'un chemin. Il est fortement conseillé de toujours utiliser cette fonction pour générer un lien et ainsi laisser Drupal en trouver la meilleur représentation URL. 
</p> 
<p>
   Voyons maintenant comment câbler notre action <kbd>diminuer</kbd>. 
</p>

<h2>Interactivité</h2> 
<p> 
    L'action <kbd>diminuer</kbd> attend que Drupal sache reconnaître un <kbd>path</kbd> de la forme <kbd>couses/diminuer/1234</kbd>. <kbd>1234</kbd> représentant le <kbd>nid</kbd> de la ligne courante. Pour cela nous allons devoir créer un nouveau menu d'un genre un peu différent du précédent, c'est à dire capable de comprendre un paramètre (le nid) et ne devant pas s'afficher dans le bloc de navigation.  
<div class='code-container-area'><div class='code-container-caption'>à insérer après le premier menu</div><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">if</span> <span class="brackets">&#40;</span><a target="blank" &nbsp;href="http://www.php.net/is_numeric"><span class="keyword">is_numeric</span></a><span class="brackets">&#40;</span>arg<span class="brackets">&#40;</span><span class="number">2</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$items</span><span class="brackets">&#91;</span><span class="brackets">&#93;</span>= <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'path'</span> =&gt; <span class="string">&quot;courses/diminuer/&quot;</span>.arg<span class="brackets">&#40;</span><span class="number">2</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'callback'</span> =&gt; <span class="string">'courses_callback_diminuer_quantite'</span>,</div></li>
<li class="li2"><div class="de2">&nbsp; &nbsp; <span class="string">'type'</span> =&gt; MENU_CALLBACK,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'callback arguments'</span> =&gt; <a target="blank" &nbsp;href="http://www.php.net/array"><span class="keyword">array</span></a> <span class="brackets">&#40;</span>arg<span class="brackets">&#40;</span><span class="number">2</span><span class="brackets">&#41;</span><span class="brackets">&#41;</span>,</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="string">'access'</span> =&gt; <span class="keyword">TRUE</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p> 
   Plusieurs points méritent attention. Tout d'abord, la fonction <kbd>arg(n)</kbd>. C'est une fonction Drupal qui renvoie le morceau du <kbd>path</kbd> courant se trouvant à la position <kbd>n</kbd> (n débutant à 0). Pour le <kbd>path</kbd> <kbd>courses/retirer/1234</kbd>, <kbd>1234</kbd> correspond donc à <kbd>arg(2)</kbd>. 
</p> 
<p> 
   Pour ne pas surcharger la liste des menus de Drupal, nous n'ajoutons ce nouveau menu <b>que lorsque</b> le troisième argument, <kbd>arg(2)</kbd>, est une valeur numérique. N'oubliez pas que le code de Drupal est exécuté à chaque requête utilisateur. Du coup, tous les <kbd>hook_menu</kbd> de tous les modules actifs sont balayés. Il est donc important d'optimiser au maximum. </p>
<p>
Ensuite nous fabriquons un <kbd>path</kbd> dynamique à l'aide de cet argument et nous précisons à Drupal de passer cet argument en paramètre de la fonction de callback <kbd>courses_callback_diminuer_quantite</kbd>. Enfin, nous utilisons le type <kbd>MENU_CALLBACK</kbd> qui va nous permettre de créer un menu qui ne soit pas affiché dans le block de navigation. </p> 
<p>Il ne nous reste plus qu'à écrire cette fonction de callback : 
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1"><span class="keyword">function</span> courses_callback_diminuer_quantite<span class="brackets">&#40;</span><span class="0">$nid</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; <span class="0">$node</span>=node_load<span class="brackets">&#40;</span><span class="0">$nid</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">if</span> <span class="brackets">&#40;</span><span class="0">$node</span>-&gt;<span class="method">quantite</span>&gt;<span class="number">0</span><span class="brackets">&#41;</span> <span class="brackets">&#123;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="0">$node</span>-&gt;<span class="method">quantite</span>--;</div></li>
<li class="li2"><div class="de2">&nbsp; <span class="brackets">&#125;</span></div></li>
<li class="li1"><div class="de1">&nbsp; node_save<span class="brackets">&#40;</span><span class="0">$node</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1">&nbsp; <span class="keyword">return</span> courses_callback_liste<span class="brackets">&#40;</span><span class="brackets">&#41;</span>;</div></li>
<li class="li1"><div class="de1"><span class="brackets">&#125;</span></div></li></ol></div></div></div> 
</p> 
<p> 
   Comme tout menu, la fonction callback doit renvoyer un résultat à afficher, nous poursuivons donc le traitement en appelant la callback qui affiche le menu. 
</p>
<p>
Le traitement effectué par cette fonction de callback consiste à charger dans l'objet <kbd>$node</kbd> le node dont le <kbd>nid</kbd> est passé en paramètre. Ensuite à décrémenter sa quantité puis à sauver le objet <kbd>$node</kbd>. Notez que cela aurais pu être fait en une seule requête SQL, sans passer par Drupal mais cela aurais été moins instructif. 
</p>

<h2>Conclusion</h2> 
<p> 
    Voilà, nous avons terminé notre premier module qui avec moins de 200 lignes de code, permet de faire tout ce que nous voulions. Il est évidemment possible d'aller beaucoup plus loin, d'ajouter des champs dans la liste pour simplifier l'édition par exemple ou encore utiliser des callback en ajax pour réduire le nombre d'items. 
</p> 
<p> 
   Quoi qu'il en soit, j'espère vous avoir montré à quel point, une fois les notions de base assimilées (hooks, nouveau type de contenu, menus, etc.) il est simple de forger un module qui corresponde parfaitement à un besoin particulier. 
</p> 
    ]]></content>
  </entry>
  <entry>
    <title>VirtualBox 1.6 sous Mandriva</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1534" />
    <id>http://artisan.karma-lab.net/node/1534</id>
    <published>2008-05-14T15:03:01+02:00</published>
    <updated>2008-05-17T14:26:30+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Linux" />
    <category term="mandriva" />
    <category term="Planet Libre" />
    <category term="Tutoriels" />
    <summary type="html"><![CDATA[<p>
   Alors que je mettais à jour mon miroir des différents dépôts Mandriva 2008.1, je découvre qu'est apparu dans main.backports la nouvelle mouture de VirtualBox (1.6).
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
   Alors que je mettais à jour mon miroir des différents dépôts Mandriva 2008.1, je découvre qu'est apparu dans main.backports la nouvelle mouture de VirtualBox (1.6).
</p>
<!--break-->
<p>
 Tout content, je commence simplement par mettre à jour (urpmi --auto-update) et je lance <kbd>VirtualBox</kbd>. Et là... patatra... Déjà, l'outil me prévient qu'il compte convertir ma VM pour la nouvelle version, candide, j'accepte. Ensuite, il s'arrête brutalement me déclarant que mes drivers ne sont pas à la bonne version. 
</p>
<p>
  Je vérifie par un petit coup de <kbd>modinfo vboxdrv</kbd> et en effet, c'est encore une 1.5.6. Je fais un <kbd>rpm -qa | grep box</kbd>, et là, j'ai un joli mix de 1.5.6 et de 1.6. Je décide de désinstaller l'ensemble par un sauvage <kbd>urpme $(rpm -qa | grep box)</kbd> (faite gaffe, il se trouve que je n'ai pas d'autre paquets qui contiennent le mot <kbd>box</kbd>). Et je relance une installation (<kbd>urpmi virtualbox</kbd>). Et là je m'aperçoit que c'est le paquet <kbd>dkms-virtualbox</kbd> qui coince, il m'insulte en disant que mon <kbd>/lib/modules/blabla/build</kbd> ne contient pas le bon kernel, ou quelque chose d'approchant.  
</p>
<p>
   Du coup, je me fâche, et je vire le paquet de source, dkms, dkms-virtualbox et virtualbox. Je vérifie qu'il ne reste rien dans <kbd>/usr/src</kbd> et je lance <kbd>urpmi kernel-source-latest dkms dkms-virtualbox</kbD>. Et miracle, ça passe cette fois. Sûrement un problème de ménage dans l'arborescence dkms. Bref, je vérifie par un <kbd>modinfo vboxdrv</kbd> que cette fois j'ai bien la bonne version du pilote et satisfait, je lance un <kbd>urpmi virtualbox</kbD> pour relancer enfin ma vm. 
</p>
<p>
  Et là, recoinçage, cette fois VirtualBox me dit que je n'ai aucun droit pour utiliser le driver (qui cette fois est le bon). 
<div class='code-container-area'><div class='code-container'><div class="code"><ol><li class="li1"><div class="de1">The VirtualBox kernel driver is not accessible to the current user. Make sure that the user has write permissions for /dev/vboxdrv by adding them to the vboxusers groups. You will need to logout for the change to take effect..</div></li>
<li class="li1"><div class="de1">VBox status code: -1909 (VERR_VM_DRIVER_NOT_ACCESSIBLE).</div></li></ol></div></div></div>
</p>
<p>
  Là, je me dis que c'est dkms qui a monté le module en mémoire. Peut être que si je redémarre le service, le script <kbd>/etc/init.d/virtualbox</kbd> va magouiller deux trois choses en plus qui permette à un utilisateur standard de touche au module. Je lance donc un <kbd>service virtualbox restart</kbd>. Content de mes deux <kbd>[OK]</kbd>, je relance une dernière fois ma VM, et là, miracle.. ça marche... 
</p>
<p>
   Je vais donc pouvoir utiliser cette nouvelle version de VirtualBox en me demandant comment un nouvel entrant, ex-windowsien, peut vivre ce genre d'aventure...
</p>

    ]]></content>
  </entry>
</feed>
