Créer un module Drupal, les blocks
Le 8 octobre 2008, à 15:2 par Ulhume...

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 sont disponibles couvrant à peu prés tous les usages.

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.

Ce tutoriel est la seconde version d'une précédente mouture que j'ai cherché à intégrer comme première étape de la série Créer un module Drupal. Comme toujours, l'ensemble des sources de ce tutoriel est disponible ici avec en plus les archives pour éviter d'avoir à utiliser subversion.

Prérequis

Les seuls prérequis pour développer un module sont une bonne connaissance des concepts clés de Drupal (blocks, node, type de contenu, taxonomy, etc.), une relative maîtrise de PHP.

Armé de tout cela, nous allons commencer en douceur avec la création d'un module dont le seul but est de nous fournir un bloc avec du texte dedans, rien de plus. Ce module 'Liste des courses', sera ensuite étendu pour en faire un module complet de gestion de vos achats quotidien. Tant qu'à prendre un exemple, autant qu'il serve à quelque chose (quoi ? vous n'avez pas tous un intranet domestique sous Drupal ??? Wink). Et tout cela pour Drupal 6.x.

Des modules et des hooks

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".

Un hook (en français, "Crochet") est un prototype de fonction dédié à une tâche spécifique, comme par obtenir la liste des blocs disponibles et le contenu de chacun d'entre eux.

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 d'un bloc, le prototype se nomme hook_view:

prototype du hook 'hook_view'
hook_block($op = 'list', $delta = 0, $edit = array())

Ainsi un module désirant implémenter ce hook, devra comporter le code suivant :

implémentation de 'hook_view' dans le module 'monModule'
  1.    function hook_block($op = 'list', $delta = 0, $edit = array()) {
  2. // Traitement à effectuer sur le module
  3.    }

Lorsque Drupal aura besoin d'obtenir la liste des blocs, il cherchera dans la liste de toutes les fonctions PHP disponibles, c'est-à-dire les fonctions contenues dans tous les modules activés, celles dont le nom commence par le nom d'un module et se termine par _block, le nom du hook.

La manière dont Drupal utilise un hook est assez simple. Pour rester sur notre exemple, lorsqu'il a besoin d'obtenir la liste des blocs, il va appeler tout les fonctions _block qu'il trouve, les unes après les autres, en leur passant en premier paramètre la chaîne list. Et réponse, chacune va donner un tableau contenant les noms et descriptions des blocs qu'elles prennent en charge.

Ensuite lorsque Drupal va devoir afficher un bloc en particulier, il effectue la même opération mais cette fois en passant en paramètre view suivi du nom du bloc.

Maintenant au delà du mode d'exécution des hooks de même type, chacun est spécifique tant par les paramètres qu'il reçoit que par les valeurs que Drupal s'attend à recevoir. Vous pouvez obtenir leur description dans la documentation de l'API Drupal.

Structure d'un module

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 étendre son comportement. Maintenant de manière plus prosaïque, un module est simplement un dossier qui contient au minimum deux fichiers.

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 modules/ mais vous seriez vite embêtés lors d'un changement de version. Il est donc préconisé de mettre cela dans un dossier sites/all/modules/mes_modules que vous créerez si nécessaire. Ainsi lorsque vous changez de version, il suffit de déplacer le dossier sites à sa place dans la nouvelle arborescence.

Dans ce dossier mes_modules, vous allez donc créer le sous-dossier du module lui-même, dans notre cas courses. 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 : courses.info et courses.module. Ce qui nous donne l'arborescence suivante :

  1.   modules
  2.   ... etc ...
  3.   site
  4.     all
  5.       modules
  6.         contributions
  7.         ...etc...
  8.         mes_modules
  9.            courses
  10.             courses.info
  11.             courses.module
  12.           ...etc...
  13.       themes
  14.     ...etc...

Fichier .info

Le fichier statistiques.info 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 :

courses/courses.info
  1. name = "Liste des courses"
  2. description = "Gestion de la liste des courses"
  3. package = "karma-lab - exemples"
  4. project = "courses"
  5. version = "6.x-1.0"
  6. core = 6.x

package indique dans quelle catégorie le module doit être affiché. name, description sont des informations textuelles qui seront affiché dans le panneau d'administration des modules. name reprend le nom du module (qui est aussi le nom du dossier). version indique quant à lui la version du module. C'est d'ailleurs plus une norme qu'autre chose qui se lit "Module version 0.1 pour Drupal 6.x". Enfin core prévient Drupal que le module est seulement compatible avec sa version 6.x. Tout autre version de Drupal empêchera le module de s'activer.

Si nous avions eu besoin de définir une dépendance avec un autre module, nous aurions ajouté une clause de la forme dependencies[] = autre_module. Enfin, si vous désirez ajouter des commentaires, il suffit d'utiliser le symbole ; suivi du texte du commentaire.

Fichier .module

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 :

courses/courses.module
<?php


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 http://mon_site_drupal/?q=/admin/build/modules pour le voir apparaître dans la liste. Vous constatez que les informations affichées sont les champs name et description de notre fichier courses.info.

A ce stade nous pourrions donc activer notre module, mais ne le faites pas tout de suite, nous avons encore quelque chose à rajouter.

Implémentation de hook_block

Il est maintenant temps d'implémenter notre premier hook. Comme nous l'avons vu plus haut, celui qui est responsable de la génération de block est hook_block. Il nous faut donc ajouter son implémentation à la suite, dans notre fichier statistiques.modules:

à ajouter à courses.module
  1. function courses_block($op = 'list', $delta = 0, $edit = array()) {
  2.   $blocks=array();
  3.   // Enumeration des blocs disponibles
  4.   if ($op == 'list') {
  5.     $blocks['courses'] = array(
  6.       'info' => t('Les courses en attente'));
  7.     return $blocks;
  8.   }
  9.  
  10.   // Génération du formulaire de configuration
  11.   else if ($op == 'configure' && $delta == 0) {
  12.     $form['courses_nombre_item_max'] = array(
  13.       '#type' => 'textfield',
  14.       '#title' => t("Combien d'item affiché au maximum ?"),
  15.       '#default_value' => courses_nombre_item_max(),
  16.     );
  17.     return $form;
  18.   }
  19.  
  20.   // Sauvegarde du formulaire de configuration
  21.   else if ($op == 'save' && $delta == 0) {
  22.     variable_set('courses_nombre_item_max', $edit['courses_nombre_item_max']);
  23.   }
  24.  
  25.   // Génération du contenu à afficher pour le block
  26.   else if ($op == 'view') {
  27.     switch($delta) {
  28.       case 'courses': // Il s'agit bien de notre bloc
  29.         $block = array(
  30.           'subject' => t('Liste des courses'),
  31.             'content' => courses_contenu());
  32.         break;
  33.     }
  34.     return $block;
  35.   }
  36. }
  37.  
  38. function courses_nombre_item_max() {
  39.   return variable_get('courses_nombre_item_max',5);
  40. }

Le paramètre important de ce hook est $op. Lorsque Drupal a besoin de la liste des blocs disponible, il appelle cette fonction avec $op=='list'. Drupal s'attend alors à ce que la fonction renvoie un tableau associant un id (ici courses) à une définition de bloc. Cette définition est elle aussi un tableau associant une propriété du bloc à sa valeur. Cette organisation des données est un grand classique dans Drupal.

Ici nous renvoyons à Drupal un seul bloc ayant pour description (propriété info) affichage des statistiques. Notez l'utilisation de la fonction t(...) qui permet de prévoir une prise en charge ultérieure de la traduction du module. Pensez à utiliser systématiquement cette fonction pour tous les textes.


Vous pouvez dés maintenant voir le hook en action en allant sur l'URL http://mon_site_drupal?q=admin/build/block. Vous devriez alors le voir apparaître dans la liste des blocs désactivés.

Passons maintenant aux opérations $op==configure et $op==save. La première fabrique un mini formulaire Drupal. La seconde sauvegarde le résultat du formulaire modifié par l'utilisateur.

Le formulaire est construit dans la variable $form dont la structure de stockage est la même que pour $blocks. C'est à dire un double tableau associatif, contenant la liste par id des champs. Puis par champ, la liste des propriétés et leurs valeurs.

La propriété #type détermine le type du champ (zone de texte,liste déroulante, etc). Ici nous choisissions une case à cocher (checkbox). La liste de complète des types et de leurs propriétés associées est disponible dans la documentation de Drupal.

Mais la propriété vraiment intéressante est #default_value. Elle définit la valeur que dois prendre le champ au moment de l'affichage du formulaire. Cette valeur est extraite des propriétés générales de Drupal par la fonction variable_get(...), par l'intermédiaire de la fonction courses_nombre_item_max(). En effet, Drupal met à disposition des modules un système de variables persistantes très simple à mettre en oeuvre. Passer par une fonction intermédiaire nous permet seulement de définir la valeur par défaut ((ici 5) de cette variable à un seul endroit et d'utiliser cette fonction partout où nous auront besoin de ce paramétrage.

La dernière propriété #title définit le titre du champ. Là aussi nous utilisons la fonction t(...) en prévision d'éventuelles traductions à venir.


Notre micro formulaire est maintenant prêt à être utilisé par Drupal. Vous pouvez le tester en cliquant sur le lien configurer dans la page d'administration, à la fin de la ligne du bloc statistiques, après sa description.

Lorsque vous aurez paramétré votre bloc, le fait de cliquer sur le bouton Enregistrer le bloc provoquer un nouvel appel de notre hook, mais cette fois avec le paramètre save renvoyant les résultats du formulaire définit à l'étape précédente. Ces résultats sont contenus dans le tableau associatif $edit et notre seule tâche est d'utiliser la fonction Drupal permettant de stocker une variable persistante, variable_set(...);, en lui passant la valeur du champ statistiques_commentaires.

La dernière étape est la génération du code HTML du bloc. C'est le rôle de l'opération $op==view qui utilise le paramètre du hook $delta.

hook_block permettant de créer autant de blocs que désiré, $delta contient l'ID du bloc à créer. Ici nous n'en avons qu'un mais autant prévoir l'avenir et effectuer tout de même le test. Ensuite, nous définissons une fois de plus un tableau associatif contenant deux propriétés, le titre du node (subject) et le contenu HTML du node (content).

Il est maintenant temps d'afficher notre bloc en sélectionnant une position dans la page d'administration. Une fois ce paramétrage sauvegardé (bouton enregistrer les blocs, le nouveau bloc devrait apparaître là où vous l'avez décidé.

Contenu du bloc

Maintenant que notre bloc est prêt, passons à l'affichage de son contenu. Pour cela nous allons remplacer la ligne content' => 'Un bloc vide'); par 'content' => statistiques_contenu()); et ajouter la fonction de rendu HTML de notre bloc. Bien évidemment, vu que nous n'avons pour l'instant pas de base de données contenant la liste réelle de nos courses, nous allons mettre dans ce bloc des items poétiques :

à ajouter à courses.module
  1. function courses_contenu() {
  2.   $result="<ul class='menu'>";
  3.   $liste=array(
  4.   array('quantite'=>1,'description'=>"triperie"),
  5.   array('quantite'=>2, 'description'=>"pierre"),
  6.   array('quantite'=>3, 'description'=>"fleurs"),
  7.   array('quantite'=>1, 'description'=>"oiseau"),
  8.   array('quantite'=>22, 'description'=>"fossoyeurs"),
  9.   array('quantite'=>1, 'description'=>"amour"),
  10.   array('quantite'=>1, 'description'=>"raton laveur"),
  11.   array('quantite'=>1, 'description'=>"madame untel"),
  12.   array('quantite'=>1, 'description'=>"citron"),
  13.   array('quantite'=>1, 'description'=>"pain"),
  14.   array('quantite'=>1, 'description'=>"grand rayon de soleil"),
  15.   array('quantite'=>2, 'description'=>"lame de fond"),
  16.   array('quantite'=>1, 'description'=>"pantalon"),
  17.   array('quantite'=>3, 'description'=>"porte (avec son paillasson)"));
  18.   $nombre=1;
  19.   $max=courses_nombre_item_max();
  20.   foreach ($liste as $item) {
  21.     if ($nombre > $max) {
  22.       break;
  23.     }
  24.     $result.="<li class='leaf'>".t($item['description']).' ('.$item['quantite'].")</li>";
  25.     $nombre++;
  26.   }
  27.   return $result;
  28. }

Voilà, rien de sorcier dans cette fonction, juste la création de code HTML que l'on renvoie à hook_block. Il ne reste maintenant plus qu'à apprécier le résultat.

Conclusion

Voilà, nous avons terminé notre premier module totalement fonctionnel en espérant que vous avoir montré à quel point, une fois quelques notions de base assimilées Drupal est facile à adapter à des besoins même très particuliers.

Commentaires

anti-pixel , le 15 May, 2008 - 19:36

Ca m'a l'air d'être un très bon tuto que je vais tester très bientôt...

Ulhume, le 15 May, 2008 - 20:45

Et bien écoute merci Smiling C'est un des plus long que j'ai eu à réaliser et il est encore incomplet à mon sens. Il lui manque :
- Une vue custom pour un node
- La validation des formulaires, le problème est que dans mon exemple je n'ai rien à valider Wink
- La gestion des droits
- Un panneau d'administration associé

C'est pour cela qu'il est encore en béta, avec cela en plus, ça devrait être complet. Si j'ai oublié quelque chose, ne pas hésiter !!

anti-pixel , le 16 May, 2008 - 14:59

16h00 je m'ennuie au boulot, et j'ai une heure à tuer, je commence le tuto Glad

anti-pixel , le 16 May, 2008 - 15:49

45mn plus tard, et bien que cette fin de semaine me laisse que très peu de neurones, je te donne, mon avis...
J'ai suivi (bêtement) le tuto sans trop réfléchir, et j'ai un résultat qui fonctionne relativement (et qui marcherait parfaitement si je m'étais appliqué). Selon moi, ce n'est pas une bonne idée de rajouter tous les élements dont tu parles à la suite mais plutot comme une seconde partie du tuto (l'idée étant de fournir différents niveaux pour aider à la compréhension par échelon). Un autre truc sympa serait de fournir les fichiers de ton module pour les fainéants qui sont fatigués du copier-coller Glad

Et puis sinon (dsl), attention aux fautes Glad

Voila, tuto très utile pour les dev drupal !

Ulhume, le 16 May, 2008 - 16:41

@Antipixel tu parles de fainéants qui auraient du mal à lire ? Wink)) Bon, j'avoue que c'est pas super facile à voir, juste après la conclusion, tu as un petit encadré :
L'ensemble des sources de ce tutoriel est disponible ici.

Là tu as accès à tous les sources sur un serveur subversion.

Sinon, ok pour le tuto "niveau avancé", c'est effectivement une bonne idée.

En tout cas merci pour ton aide.

PS: pour l'orthographe j'atteins là mon seuil d'incompétence. J'appartiens à un bout de génération qui en france est passé dans une faille temporelle d'apprentissage du sujet. l'époque les instit's n'avaient que le mot "éveil" à la bouche... j'ai découvert le concept de "dictées" un peu tard... Si tu as un tuto pour moi à ce sujet je suis preneur ! Smiling

anti-pixel , le 17 May, 2008 - 14:42

mea culpa, j'avais pas vu le petit encadré pour les sources Glad
lol j'ai pas de tuto pour l'orthographe dsl, je n'y fais pas trop attention dans les messages et les petits posts mais un peu plus sur les gros tutos Wink

Anonymous , le 20 July, 2008 - 13:17

Bonjour,
Avant tout merci pour ce tuto. Je me sens un peu moins seul...
J'ai suivi à la lettre vos instructions :
"A ce stade nous pourrions donc activer notre module, mais ne le faites pas tout
de suite, nous avons encore quelque chose à rajouter. ==> OK

j'ai continué :
"Vous pouvez dés maintenant voir le hook en action en allant sur l'URL
http://mon_site_drupal?q=admin/build/block. Vous devriez alors le voir apparaître
dans la liste des blocs désactivés."
==> Dans mon cas, rien n'apparait.

J'ai poursuivi cependant :
"Notre micro formulaire est maintenant prêt à être utilisé par Drupal. Vous
pouvez le tester en cliquant sur le lien configurer dans la page
d'administration, à la fin de la ligne du bloc statistiques, après sa
description."
==> rien n'apparait encore une fois

Merci de votre aide afin que je puisse continuer à avancer dans ma longue, difficile mais passionnante aventure de création d'un web !!!

Je souhaite également mettre gallery2 dans drupal. La config s'est bien passée mais au chargement de mon web je ne vois pas d'accès à la gallerie !
Existe-t-il un tuto dans le style du votre sur le sujet ?

Bien à vous
Onurbski

Ulhume, le 20 July, 2008 - 23:03

Quelle version de Drupal utilisez-vous ? Est-ce un apache que vous hébergez ou mutualisé ?

Sinon, une bonne manière de tester d'où cela peu venir est de coller une trace en début de fonction "statistiques_block", quelque chose du genre
error_log("======> ".$op);

Et de regarder dans les traces apache si cela appairait.

onurbski, le 20 July, 2008 - 23:39

J'utilise :
web server : zazoumiwebserver 1.2.8
DB : mysql 5.0.51 community
php version : 5.2.5 cgi-fcgi
Gallery2 : 2.2.5
OS : XP
Browser : Firefox 2.0.0.16

Pour l'instant, je suis en local sur mon PC et je fais des tests avant déploiement sur free.
J'ai pu créer un article sur drupal.
J'ai pu créer des albums sur gallery2.
Le problème est qu'au lancemnt de drupal je vois en première page du web mon article mais je ne vois rien pour avoir accès à gallery2.
J'ai mis gallery2 sous ...DRUPAL\sites\all\gallery2.
Etant tout débutant, je patauge mais persévère. Savez-vous s'il existe des tutos avec exemple pratique ?
Merci
Onurbski

Ulhume, le 21 July, 2008 - 06:41

C'est de la version de DRUPAL dont j'avais besoin Smiling

Pour gallery2, je n'ai vraiement jamais utilisé, mais en toute logique cela doit marcher par node, comme les articles. Donc si vous fait un ?q=admin/content/node est-ce que vous voyez votre galerie dans la liste. Normalement, elle devrait avoir un "type" particulier. Et si oui, pouvez vous l'afficher en cliquant sur la 1iere colonne ?

Sinon, une possibilité classique est une erreur sur le système de fichier, pour cela, allez dans ?q=admin/reports/status et vérifiez que vous n'avez pas d'erreur affichée. Corrigez le cas échéant ?

Sinon pour le tuto sur les blocs, vous avez essayé les traces ?

Ben , le 13 August, 2008 - 15:47

Pour répondre à ce problème :

**************
"Vous pouvez dés maintenant voir le hook en action en allant sur l'URL
http://mon_site_drupal?q=admin/build/block. Vous devriez alors le voir apparaître
dans la liste des blocs désactivés."
==> Dans mon cas, rien n'apparait.
**************

=> Il faut activer le module créé (?q=admin/build/modules) pour visualiser le bloc

Ulhume, le 13 August, 2008 - 22:50

@Ben il me semble que c'est précisé dans le tuto ?

charly , le 19 August, 2008 - 20:47

bonjour,

on accede comment a l'url du module ?
http://mon_site_drupal?q=statistiques

car avec cette url cela ne fonctionne pas

J'aimerais developper un module afin de gerer la liste des membres de mon site www.varouler.com
car la liste d'origine n'a pas de mise en page.

J'ai telecharger le module user_liste mais celui ci n'affiche pas les fotos en mini.
Je voudrais donc le modifier.

merci de votre aide
charlyy

Ulhume, le 24 August, 2008 - 08:12

Que veux tu dire par "URL du module" exactement ?

Poster un nouveau commentaire

Le contenu de ce champ est gardé secret et ne sera pas montré publiquement.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • To highlight piece of code, just surround them with <code type="language"> Your code &tl;/code>>. Language can be java,c++,bash,etc... Everything Geshi support.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Textual smileys will be replaced with graphical ones.
  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.

Plus d'informations sur les options de formatage

Connexion utilisateur
Les derniers bavardages...