Création de modules : un tutoriel Drupal 6.x

Référence en anglais sur drupal.org : 18 Juin 2009 - 23h01 - http://drupal.org/node/206753


Ce tutoriel explique comment créer un module pour Drupal 6. C'est une mise à jour du tutoriel pour Drupal 5. Une grande partie de ce tutoriel est aussi valable pour Drupal 5, mais vous devriez vérifier la documentation de l'API ainsi que la documentation sur comment mettre à jour vos modules d'une version Drupal vers une autre.

Un module est une collection de fonctions qui s'enchaînent dans Drupal, lui fournissant des fonctionnalités supplémentaires. Après la lecture de ce tutoriel, vous pourrez créer un module pour un bloc élémentaire et l'utiliser comme modèle pour des modules plus sophistiqués et des modules de nodes.

Ce tutoriel ne vous préparera probablement pas à l'écriture de modules que vous lâcherez dans la nature. Il ne traite pas des mises en cache ni des questions de sécurité ou des droits. Utilisez-le comme point de départ et jetez un œil aux autres modules, au Manuel Drupal, à la documentation sur l'écriture de code sécurisé et sur les Normes de programmation.

Ce tutoriel suppose que vous avez :

  • des connaissances de base en PHP, y compris sur la syntaxe et les concepts d'objets PHP,
  • une certaine compréhension des tables de bases de données, des champs, enregistrement et des instructions SQL,
  • une installation Drupal en fonctionnement,
  • un accès à un serveur web.

Ce tutoriel ne part pas du principe que vous connaissez le fonctionnement interne d'un module Drupal. Il ne vous aidera pas à écrire des modules pour les versions Drupal antérieures à la 5.

Pour la mise en cache, voir Cache API and caching tutorials.

Plan

Ce tutoriel expose une par une les étapes nécessaire pour la création d'un module qui affiche un bloc listant les dernières parutions de blog et de forums de discussion. Voici les grandes lignes de ce qui sera traité dans chaque page du tutoriel :

  • Démarrer: création d'un dossier pour votre module et pour le premier fichier .module
  • Communiquer votre module à Drupal : création d'un fichier .info pour votre module, et implémentation de hook_help() pour fournir au système d'aide de Drupal une brève description de votre module
  • Dire à Drupal qui peut utiliser votre module : implémentation de hook_perm(), qui déclare les types de droits pour votre module
  • Déclarer le bloc de contenu : début de l'implémentation de hook_block(), cette partie informe Drupal que le module déploie un bloc
  • Générer le bloc de contenu : fin de l'implémentation de hook_block(), la partie qui affiche le contenu du bloc. Cela implique l'exécution d'une requête sur la base de données pour récupérer les posts récent de blog et de forum, et leur affichage
  • Installation, activation et test du module : on fait bosser le module
  • Création de la page de configuration du module : modification du module pour que l'utilisateur donne un sens à « récent », en implémentant un hook_menu() et en utilisant l'API Form de Drupal pour déclarer une page de configuration.
  • Générer une page de contenu : ajouter une pleine page vue au module, dans le cas où il y aurait davantage des posts récents qui s'afficheront mieux que dans le bloc - 1ère partie : écrire une fonction qui génère la page
  • Informer Drupal de l'existence de cette nouvelle fonction : 2ème partie sur l'ajout de la page au module, utilisation de hook_menu() pour donner une URL à la page
  • Ajouter un lien « en voir plus » et afficher toutes les entrées : 3ème partie sur l'ajout d'une page d'affichage au module, associer la nouvelle URL au lien « en savoir plus » du bloc

Démarrer

Référence en anglais sur drupal.org : 16 Mars 2010 - 15h09 - http://drupal.org/node/206754


Comme indiqué dans l'introduction, nous utiliserons dans ce tutoriel l'exemple d'un module qui liste des liens vers du contenu tel que des billets de blog ou des discussions de forums récemment créés (plus exactement : créés il y a tout juste une semaine). Cette page du tutoriel expose comment créer le fichier module initial et son dossier.

Donner un nom à votre module

La première étape dans la création de module consiste à lui choisir un nom abrégé. Ce nom abrégé sera utilisé dans tous les fichiers et dans tous les noms de fonctions du module. Il doit donc débuter par une lettre et ne doit contenir, selon les conventions de nommage de Drupal, que des lettres en minuscule et des signes soulignés. pour cet exemple, nous choisirons onthisdate comme nom abrégé.

Remarque importante : si le nom abrégé est utilisé à la fois comme nom de fichier du module et comme préfixe de fonction, ce n'est pas seulement pour répondre aux conventions de nommage. Quand vous implémentez des "hooks" Drupal (voir plus loin dans ce tutoriel), Drupal ne reconnaîtra vos fonctions d'implémentation du hook que si leurs noms sont préfixés avec le nom du fichier module .

Il est également primordial que le nom abrégé porté par votre module ne soit pas déjà utilisé par un thème de votre site.

Créer un dossier et un fichier pour le module

Le choix du nom abrégé étant onthisdate étant fait, créez un dossier dans votre installation Drupal : sites/all/modules/onthisdate. Vous aurez peut-être besoin de créer d'abord un dossier sites/all/modules.

Créez ensuite un fichier PHP et sauvegardez-le sous le nom onthisdate.module dans le dossier sites/all/modules/onthisdate. Depuis Drupal 6.x, sites/all/modules est l'endroit où placer les modules qui ne font pas partie du core (et sites/all/themes l'endroit où placer les thèmes qui ne font pas partie du core), les fichiers spécifiques à un site vont dans les dossiers du site. Cela vous permet d'effectuer les mises à jour du core et des modules plus facilement, sans effacer vos personnalisations. Si vous avez une installation multi-site et que votre module n'est destiné qu'à un seul site, vous pouvez le placer dans sites/votre-dossier-du-site/modules.

Les fichiers modules débutent avec la balise d'ouverture PHP immédiatement suivie d'un balise ID CVS placée dans un commentaire inline. La balise ID CVS, $Id$, est un token que le système de contrôle de version de Drupal.org complétera avec des informations de révision et d'auteur. Il vaut mieux prendre l'habitude d'écrire cette balise dans votre module, même si vous ne pensez pas le diffuser sur Drupal.org ou pas. Une balise ID similaire est placée dans tous les autres fichiers associés à votre module.

<?php
// $Id$
?>

Cette balise ID n'est pas une syntaxe spéciale du PHP et elle n'est là que pour le système de contrôle de versions. Aussi, elle doit être placée dans une ligne de commentaire. On le répète, cette balise sera automatiquement complétée et vous pouvez désormais l'oublier.

Le module n'est pas encore opérationnel : il n'a pas été activé. Nous le ferons plus loin dans ce tutoriel.

Normes de programmation

Comme indiqué dans Normes de programmation, omettez la balise PHP fermante ?>. La mettre pourrait causer un comportement erratique sur certaines configurations de serveur (notez que les exemples du manuel montrent cette balise fermante uniquement pour des raisons de formatage de texte, vous ne devez pas la mettre dans votre code à vous).

Toutes les fonctions de votre module utilisées par Drupal sont nommées {nom_du_module}_{hook}, où hook est un suffixe de nom de fonction pré-déclaré. Drupal appellera ces fonctions pour obtenir des données spécifiques, les déclarer selon ce modèle indique à Drupal où regarder. Nous en reparlerons bientôt.

Communiquer votre module à Drupal

Traduction de la page http://drupal.org/node/206756,
publiée le 28 Septembre 2010 sur drupal.org


Sujet principal : dire à Drupal que votre module existe

Hook Drupal décrit : hook_help

Tous les modules doivent avoir un fichier nom_du_module.info qui contient des méta-informations sur le module.

Le format habituel est le suivant :

; $Id$
name = Nom du module
description = Une description sur ce qu'il fait
core = 6.x

Pour cet exemple, le fichier sera nommé onthisdate.info. Sans ce fichier, le module ne sera pas affiché dans la page Administrer » Construction du site » Modules. Pour cet exemple, il pourrait contenir ce qui suit :

; $Id$
name = On this date
description = Un module bloc qui liste des liens vers du contenu comme des billets de blogs ou des discussions de forum crées il y a une semaine.
core = 6.x

Ajoutez le code-source précédent à un fichier nommé onthisdate.info et sauvegardez-le dans le dossier module sites/all/modules/onthisdate.

Note : si vous copiez-collez ce bout de code, veillez à ce qu'il n'y ait pas de retour à la ligne dans la description (désactivez le retour à la ligne auto de votre éditeur de texte pour en être certain). Sinon, le fichier .info ne sera pas analysé correctement.

Détails du fichier Info

name (obligatoire)

Le nom de votre module qui sera affiché. Il doit respecter les normes de mise en majuscules de Drupal 6 : seule la première lettre du premier mot doit être en majuscule («Exemple module», pas «Exemple Module» ni «exemple module»)

name = On this date

description (obligatoire)

Une courte description, de préférence sur une seule ligne, qui dira, sur la page d'administration des modules, ce que fait le module. Souvenez-vous que des descriptions trop longues peuvent causer des difficultés d'utilisation de cette page, aussi soyez le plus concis possible. Ce champ est limité à 255 caractères.

description = Un module bloc qui liste des liens vers du contenu comme des billets de blogs ou des discussions de forum crées il y a une semaine.

Notez que si la description comporte des caractères spéciaux, ils doivent être remplacés par leurs entités HTML. Par exemple, utilisez description = Voici ma &quot;folle@email.com&quot; adresse e-mail au lieu de description = Voici ma "folle@email.com" adresse e-mail.

Si la description comporte des guillemets simples ou des apostrophes vous pouvez simplement mettre la chaîne de caractères dans des doubles guillemets. Par exemple : description = "S'il vous plaît, n'utilisez pas ceci à moins que vous ne sachiez ce que vous faites."

core (obligatoire)

Depuis la version 6.x, le core de Drupal refusera d'activer ou d'exécuter les modules qui ne sont pas explicitement écrits pour la bonne version du core.

Le fichier .info doit indiquer pour quelle version du core Drupal le module ou le thème ont été écrits. Ceci se fait en renseignant le nouvel attribut core du fichier .info:

core = 6.x

Remarque : le script de packaging de drupal.org renseignera automatiquement chaque release node avec cette valeur basée sur la compatibilité avec le core, ainsi les utilisateurs qui téléchargent les distributions packagées depuis drupal.org auront toujours la bonne version. Cependant, pour les sites qui déploient Drupal directement depuis le CVS, cette valeur ajoutée au fichier .info du module est utile également. C'est aussi une bonne façon d'indiquer, aux utilisateurs des modules, à quelle version du core le HEAD du CVS est compatible.

dependencies (facultatif)

Il y a quelques options supplémentaires qui peuvent apparaître dans le fichier .info, l'une d'elles sont les dépendances du module. Si un module requiert l'activation d'un ou plusieurs autres modules, énumérez les noms de fichiers de ces modules avec la syntaxe suivante :

dependencies[] = taxonomy
dependencies[] = comment

Comme cela ne s'applique pas à notre module exemple, nous les avons omises. Si des dépendances sont assignées à un module, Drupal ne permettra pas son activation tant qu'elles ne seront pas satisfaites.

package (facultatif)
Si un module fait partie d'un groupe, la page Administrer » Construction du site » Modules l'affichera avec les autres modules du même groupe. Si aucun groupe n'est attribué, il sera listé dans le groupe Autres. Ne pas assigner de groupe à votre module est tout à fait possible; en règle générale les groupes sont utilisés pour les modules distribués ensemble ou faits pour être utilisés ensemble. Dans le doute, laissez ce champ vide.

package = "le nom de votre groupe, au choix"

Voici quelques suggestions de groupes possibles :

  • Audio
  • Bot
  • CCK
  • Chat
  • E-commerce
  • Event
  • Feed Parser
  • Organic Groups
  • Station
  • Video
  • Views
  • Voting (s'il utilise/a besoin de VotingAPI)

Le fichier utilise le format INI et peut avoir un ; $Id$ pour que le CVS insère les données ID au fichier.

Pour plus d'informations sur le format du fichier INI, voir la documentation PHP.

Aide via le hook

Vous pouvez aussi fournir de l'aide et des informations supplémentaires sur votre module en implémentant la fonction Drupal hook_help(). Étant donné la description du fichier .info qui précède, ce hook est désormais facultatif. Cependant, c'est une bonne idée de l'implémenter.

Pour cela, remplacez hook dans le nom du hook par le nom bref de votre module, et créez une fonction dans le fichier .module avec ce nom.

Par exemple, pour implémenter hook_help() dans notre module exemple, nous créons dans le fichier onthisdate.module une fonction appelée onthisdate_help() :


<?php
function onthisdate_help($path$arg) {

}
?>

Le paramètre $path fournit le contexte de l'aide : à quel endroit de Drupal ou du module se trouve l'utilisateur quand ils accèdent à l'aide. Le traitement conseillé pour cette variable se fait dans une instruction switch. Ce modèle de code est habituel dans les modules Drupal. Voici un court exemple d'implémentation de cette fonction :

<?php

/**
* Display help and module information
* @param path which path of the site we're displaying help
* @param arg array that holds the current path as would be returned from arg() function
* @return help text for the path
*/
function onthisdate_help($path$arg) {
  
$output '';  //declare your output variable
  
switch ($path) {
    case 
"admin/help#onthisdate":
      
$output '<p>'.  t("Displays links to nodes created on this date") .'</p>';
      break;
  }
  return 
$output;
// function onthisdate_help

?>
(notez que la balise PHP fermante ne doit pas figurer dans votre code-source)

Le case de case "admin/help#nom_du_module": est utilisé par le core de Drupal pour linker depuis la page d'aide principale (/admin/help ou ?q=admin/help). le cas échéant, vous fournirez une aide plus conséquente à l'utilisateur en ajoutant plus de texte.

Voir aussi

Spécifier les droits disponibles

Référence en anglais sur drupal.org : 2 Avril 2010 - 19h39 - http://drupal.org/node/206757


La prochaine fonction à écrire est la fonction des droits d'accès, implémentant la fonction Drupal hook_perm(). C'est là que vous déclarerez les noms des droits de votre module - il ne s'agit pas d'accorder des droits ou de déclarer la portée des droits, il s'agit simplement d'indiquer quels droits sont disponibles pour le module.

Comme pour le hook help de la page précédente, nous implémentons hook_perm() en créant une fonction appelée onthisdate_perm() dans le fichier module onthisdate.module.

Le rôle de cette fonction sera juste de renvoyer une liste énumèrant les noms des droits d'accès utilisés par le module; une fois que vous aurez déclaré les droits dans l'implémentation de hook_perm(), un administrateur pourra définir les rôles ayant ces droits dans la page Administrer » Gestion des utilisateurs » Droits d'accès.

Par exemple, pour créer un droit appelé «access onthisdate content », nous créerons la fonction suivante :

<?php
/**
* Valid permissions for this module
* @return array An array of valid permissions for the onthisdate module
*/
function onthisdate_perm() {
  return array(
'access onthisdate content');
// function onthisdate_perm()
?>

Note pour le débutants : la balise ouvrante <?php n'apparaît qu'une fois dans votre fichier module, au début, et la balise fermante ?> ne doit pas figurer du tout dans votre fichier.

Si vous écrivez un module qui a besoin de gérer les droits plus finement, vous pouvez les ajouter :

<?php
return array('access onthisdate content''administer onthisdate');
?>

Pour ce tutoriel, ne mettez qu'un droit, nous en ajouterons ultérieurement.

Le contenu de la chaîne de caractères qui décrit le droit est libre mais elle doit être unique parmi celles des autres modules. Sinon, une occurrence d'un droit pourra être pris pour un autre. Pour cette raison, la chaîne décrivant le droit devrait comporter le nom du module afin d'éviter des conflits avec d'autres modules. Les conventions de nommage proposent action_verbe_nomdumodule. Utilisez l'exemple suivant comme modèle pour tous les modules que vous développerez :

<?php
function newmodule_perm() {

return array(
'access newmodule''create newmodule''administer newmodule');

// function newmodule_perm
?>

Voir aussi :

Déclarer le bloc de contenu

Référence en anglais sur drupal.org : 3 Avril 2010 - 13h24 - http://drupal.org/node/206758


Les modules sont écrits pour faire toutes sortes de choses : certains modules créent des blocs (résumés qui apparaissent souvent sur la droite ou la gauche de la page), d'autres créent des types de contenus spéciaux (pour du contenu de page, tel celui que vous lisez en ce moment), d'autres tracent des données back-end, et certains font tout ce qui précède.

Vous pourrez entendre les expressions « Block modules » pour décrire des modules qui créent essentiellement du contenu de bloc (comme le module menu) ou « Node modules » pour décrire les modules qui génèrent du contenu de page (comme les modules blog ou forums). À cette étape, ce module est un « block module », puisqu'il génère un bloc.

Dans cette page du tutoriel, le module déclarera un bloc qui affichera (après la prochaine page du tutoriel) les publications récentes de blog et du forums. Le hook pour créer les blocs s'appelle, avec beaucoup d'à-propos, hook_block. Pour l'implémenter, créez une fonction appelée onthisdate_block() dans le fichier onthisdate.module.

Voici le modèle :

<?php
/**
* Implementation of hook_block
* @param string $op one of "list", "view", "save" and "configure"
* @param integer $delta code to identify the block
* @param array $edit only for "save" operation
**/
function onthisdate_block($op 'list'$delta 0$edit = array()) { 

  
// VOTRE CODE DE GENERATION DE BLOC ICI 

// function onthisdate_block
?>

La fonction prend ces trois paramètres :

  • $op (ou operation : les fonctions hook_block() sont appelées pour réaliser quatre opérations différentes, qui sont list,, view, save et configure. Ce paramètre indique à la fonction quelle opération est appelée. Nous parlerons de l'opération list ci-dessous et de l'opération view dans la prochaine page du tutoriel. Les opération configure et save permettent à votre bloc d'avoir une page de configuration et de sauvegarder les paramètres - nous ne les utiliserons pas dans ce tutoriel.
  • $delta : votre module peut définit plus d'un bloc dans son opération list. Chaque bloc a un code delta, qui est normalement un nombre, et Drupal passe ce nombre pour les autres opérations afin que vous puissiez identifier le bloc du module sur lequel effectuer l'opération. Notre module exemple n'aura qu'un seul bloc. Le module user du core Drupal est un exemple de module qui a plusieurs blocs : bloc de login user, bloc utilisateurs récents et bloc qui est en ligne.
  • $edit : utilisé avec l'opération save seulement. Non abordé dans ce tutoriel.

La première opération est list, qui énumère les blocs fournis par le module, et déclare comment ils seront vus dans la page Administrer » Construction du site » Blocs (le module Blocks appellera cette fonction avec $op='list' lorsqu'il construit la liste des blocs pour la page d'administration des blocs)

Voici le détail de la fonction onthisdate_block() (la prochaine page du tutoriel en contient davantage) :

<?php
/**
* Implementation of hook_block
* @param string $op one of "list", "view", "save" and "configure"
* @param integer $delta code to identify the block
* @param array $edit only for "save" operation
**/
function onthisdate_block($op 'list'$delta 0$edit = array()) { 
if (
$op == "list") {
    
// Generate listing of blocks from this module, for the admin/block page
    
$block = array();
    
$block[0]["info"] = t('On This Date');
    return 
$block;
  }
// function onthisdate_block
?>

Note pour le débutants : la balise ouvrante n'apparaît qu'une fois dans votre fichier module, au début, et la balise fermante ?> ne doit pas figurer du tout dans votre fichier.

Ok, maintenant regardons ce code...

$block

$block est juste une variable utilisée pour stocker les données nécessaires, avant d'être renvoyée.

Un module peut déclarer plusieurs blocs (par exemple, un bloc qui affiche des données détaillées, et un autre bloc qui n'affiche qu'un résumé). Pour cette raison, la variable Bloc est un tableau, et chaque élément du tableau représente un bloc fourni par votre module.

$bloc[0]

Notre module exemple ne fournit qu'un bloc, qui est stocké dans l'élément 0 du tableau (le premier élément du tableau).

L'index de tableau 0 est passé à la fonction onthisdate_block via la valeur du paramètre $delta qui sera utilisée par la suite dans les traitements.

Si nous avions déclaré plusieurs blocs, nous aurions $block[0], $block[1], $block[2] etc; nous aurions également dû vérifier la valeur de $delta passée à la fonction pour les traitements ultérieurs.

$block[0]["info"]
Chaque élément (0, 1, 2) représentant un bloc est également un tableau de clé = valeurs. Certaines de ces clés sont info, cache, weight, status. Ça fait un peu beaucoup pour une première leçon, aussi nous allons traiter de info qui est le nom lisible et compréhensible par un humain qui est affiché en regard du bloc dans la page d'administration. Aussi, dans hook_block() notre bloc est connu comme $delta = 0, mais dans la page d'administration il est connu sous le nom On This Date.

La prochaine page du tutoriel traitera de la génération du bloc, par l'implémentation de l'opération view.

Voir aussi

Générer le contenu du bloc

Référence en anglais sur drupal.org : 29 octobre 2010 - http://drupal.org/node/206759


Dans cette étape du tutoriel nous générerons le contenu du bloc. Ce qui implique d'accéder à la base de données de Drupal. Notre but est d'obtenir une liste du contenu créé une semaine auparavant (ce contenu est enregistré en tant que nodes dans la base de données). Plus précisément, nous voulons le contenu créé entre minuit et 23h59 il y a tout juste une semaine. Lorsqu'un node est créé, la date et l'heure de sa création sont enregistrés dans la base de données. Nous utiliserons ce champ de la base pour trouver nos données.

Pour indiquer à Drupal le contenu que nous voulons dans le bloc, nous utiliserons l'opération view de hook_block(). Nous devrons donc ajouter un peu de code à la fonction onthisdate_block() déclarée précédemment.

En premier lieu, nous devons calculer l'heure de minuit il y a une semaine et de 23h59 il y a une semaine, en secondes. Cette partie du code n'est pas spécifique à Drupal, reportez-vous à la documentation PHP pour plus d'informations.

<?php

/**
* Generate HTML for the onthisdate block
* @param op the operation from the URL
* @param delta offset
* @returns block HTML
*/
function onthisdate_block($op='list'$delta=0) {

  if (
$op == "list") {
    
// Generate listing of blocks from this module, for the admin/block page
    
$block[0]["info"] = t('On This Date');
  }
  else if (
$op == 'view') {

    
// Generate our block content

    // Date du jour
    
$today getdate();

    
// calculer minuit il y a une semaine
    
$start_time mktime(000,
       
$today['mon'], ($today['mday'] - 7), $today['year']);

    
// nous voulons les éléments survenus seulement le jour en question, aussi  
    // calcule d'une journée
    
$end_time $start_time 86400
    
// 60 * 60 * 24 = 86400 secondes dans une journée
   

   // davantage ultérieurement...
  
}
}

?>
Si votre site n'a pas de contenu pour le jour en question, vous pouvez changer la date de fin comme ci-dessous, afin d'avoir quelque chose dans le bloc :
$end_time = time(); // obtenir toutes les publications depuis la semaine passée jusqu'à aujourd'hui

L'étape suivante est l'écriture de la requête SQL qui récupérera dans la base de données le contenu que nous voulons afficher. Nous sélectionnons du contenu de la table node, qui est la table principale pour le contenu Drupal. Avec cette requête, nous obtiendrons des contenus de tous types : billets de blog, posts sur les forums, etc. C'est tolérable dans le cadre de ce tutoriel mais pour un vrai module vous devrez préciser la requête SQL pour ne sélectionner que certains types de contenus (en ajoutant une clause WHERE sur la colonne type).

Drupal utilise des fonctions qui vous assistent dans l'exécution des requêtes SQL. Vous n'aurez ainsi à vous soucier que de l'écriture de la requête sans avoir à vous préoccuper de la cuisine interne pour la connexion à la base proprement dite. Nous utiliserons db_query() avec notre requête pour récupérer les enregistrements :

<?php
  $query 
"SELECT nid, title, created FROM " .
     
"{node} WHERE created >= '%d' " .
     
" AND created <= '%d'";

  
$query_result =  db_query($query$start_time$end_time);
?>

Cet exemple montre comment créer une requête sûre dans Drupal : création de la requête avec des paramètres comme % et %s puis passage de variables à la fonction db_query() qui prendront la place de ces paramètres. Cette façon de faire prévient les attaques par injection SQL, notamment lorsque les requêtes utilisent du contenu fourni par les utilisateurs.

Une autre habitude Drupal à suivre pour les requêtes est d'entourer les noms de tables avec des accolades ouvrantes et fermantes, comme dans {node}. C'est indispensable pour que votre module tienne compte des préfixes des tables. Vous trouverez plus d'informations sur le site Drupal en lisant la page Table Prefix (and sharing tables across instances du manuel Drupal.

Pour finir, vous remarquerez que dans cet exemple, nous ne tenons pas compte des droits d'accès pour les nodes. Normalement, toute requête sur les nodes devrait utiliser la fonction db_rewrite_sql() qui s'assure que l'utilisateur qui lit la page a les droits pour voir chaque node retourné, mais cela dépasse la portée de ce tutoriel.

Ensuite, nous utilisons db_fetch_object() pour voir chaque enregistrement retourné par la requête. Nous générerons un lien vers le node pour chaque node trouvé, avec son titre comme texte du lien :

<?php
  
// variable contenu qui sera retournée pour l'affichage   
  
$block_content ''
  while (
$links db_fetch_object($query_result)) {
    
$block_content .=  l($links->title'node/'$links->nid) .'<br />';
  }
?>

Notez que le vrai lien est créé par la fonction l(), l créé des liens <a href="link"> pour les chemins Drupal, formatés en clean URL (URL simplifiées) ou pas, selon les paramètres de la page Administrer » Configuration du site » URL simplifiées.

Pour finir, nous retournons à Drupal le contenu qui vient d'être généré, pour son affichage :

<?php
  
// vérifier s'il y a du contenu avant de retourner le block view
  
if ($block_content == '') {   
    
// pas de contenu il y a une semaine.
     
$block['subject'] = 'On This Date';
     
$block['content'] = 'Désolé, pas de contenu';
     return 
$block;
  }

  
// configurer le bloc
  
$block = array();
  
$block['subject'] = 'On This Date'
  
$block['content'] = $block_content;
  return 
$block;
}
?>

Nous retournons un tableau avec des éléments subject et content : c'est la structure attendue par Drupal pour les fonctions blocs. Si nous ne les avions pas mis les deux, le bloc n'aurait pas été correctement mis en forme.

L'exemple précédent suppose que, s'il n'y a pas de contenu pour la date choisie, vous voulez afficher un bloc disant « Désolé, pas de contenu ». A la place, vous pouvez décider d'ommettre l'affichage du bloc s'il n'y a pas de résultats. Pour cela, remplacez le code dans aux bons endroits :

<?php
  
if ($block_content == '') {   
     
/* pas de contenu il y a une semaine. Si nous ne renvoyons rien
      * le bloc ne s'affiche pas, ce qui est ce que nous voulons
     */
     
return;
  }
?>

Vous aurez peut-être remarqué que le contenu et la mise en page sont intimement liés, ce qui est fortement déconseillé. Si vous écrivez un module utilisable par tous, il vaut mieux fournir un moyen simple pour que la mise en page puisse être modifiée, notamment par des non-programmeurs. La façon de faire la plus simple consiste à ajouter un attribut de classe à votre lien, ou d'entourer le code HTML avec une balise <div> dotée d'une classe CSS spécifique et ne pas mettre la balise<br /> à la fin du lien. Une bien meilleure idée serait de rendre personnalisable le graphisme du module. Nous ne nous occuperons pas de cela pour l'instant mais gardez-le à l'esprit pour l'écriture de modules contributifs.

En mettant tous ces codes bout à bout, notre fonction ressemblera à ceci :

<?php
function onthisdate_block($op='list'$delta=0) {

  if (
$op == "list") {
    
// Génère le listing des blocs pour ce module, pour la pages Admin/Blocs
    
$block = array();
    
$block[0]["info"] = t('On This Date');
    return 
$block;
  }
  else if (
$op == 'view') {

    
// Génère notre contenu du bloc

    // la variable content sera retournée pour l'affichage
    
$block_content '';

    
// Obtenir la date du jour
    
$today getdate();

    
// calculer minuit il y a une semaine
    
$start_time mktime(000,$today['mon'],
                               (
$today['mday'] - 7), $today['year']);

    
// nous voulons seulement les éléments du jour en question, 
    //alors on calcule un jour
    
$end_time $start_time 86400;
    
// 60 * 60 * 24 = 86400 secondes dans une journée

    
$query "SELECT nid, title, created FROM " .
             
"{node} WHERE created >= '%d' " .
             
" AND created <= '%d'";

    
$query_result =  db_query($query$start_time$end_time);
    while (
$links db_fetch_object($query_result)) {
      
$block_content .= l($links->title'node/'.$links->nid) . '<br />';
    }

    
// vérifier s'il y a du contenu, avant de retourner la vue bloc
    //  
    
if ($block_content == '') {
    
// pas de contenu il y a une semaine.
     
$block['subject'] = 'On This Date';
     
$block['content'] = 'Désolé, pas de contenu';
     return 
$block;
    }
    
// configurer le bloc
    
$block['subject'] = 'On This Date';
    
$block['content'] = $block_content;
    return 
$block;
  }
}  
// end onthisdate_block
?>

Notre module est maintenant opérationnel (bien qu'il resterait encore à rendre personnalisable le style de ses affichages mais nous n'aborderons pas le sujet dans cet article), nous pouvons l'installer et le tester (prochaine étape du tutoriel).

Voir aussi

Installation, activation et test du module

Référence en anglais sur drupal.org : 24 août 2009 - 23h30 - http://drupal.org/node/206760


A ce stade du tutoriel, vous pouvez installer votre module et il fonctionnera. Faisons-le et voyons où nous aurons besoin de l'améliorer.

Installer

Pour installer le module, vous devrez copier les fichiers onthisdate.module et onthisdate.info dans le bon dossier de votre site, s'ils ne s'y trouvent pas déjà (probablement dans le dossier sites/all/modules/onthisdate -- voir la première page de ce tutoriel.

Activer

Connectez-vous en tant qu'administrateur et affichez la page d'administration des modules pour avoir leur affichage par ordre alphabétique : Administrer » Constructions du site » Modules; Ou via l'URL :

  • http://exemple.com/admin/build/modules
  • http://example.com/?q=admin/build/modules

En faisant défiler la page, vous verrez le module onthisdate et sa description dans le groupe Autres. Activez le module en cochant la case à cocher et enregistrez la configuration.

Configurer

Le but de ce module est d'afficher un bloc, mais la seule activation du module ne provoquera pas l'affichage du bloc. Vous devrez activer le bloc dans la page d'administration des blocs (Administrer » Construction du site » Blocs).

Activez le bloc en sélectionnant une région dans la liste déroulante du bloc OnThisDate et sauvegardez en cliquant sur Enregistrer les blocs.

Vérifiez bien l'emplacement (gauche/droite) si vous utilisez un thème qui restreint l'affichage des blocs. Après avoir sauvegardé le paramétrage de la région, vous voudrez peut-être modifier la configuration du bloc (en cliquant sur configurer de la colonne opérations) pour qu'il ne s'affiche que sur certaines pages du site ou pour certains rôles seulement. Ayez en tête le fait que si vous êtes connecté en tant qu'utilisateur 1 (le premier compte créé lorsque vous avez installé Drupal), il n'y aura pas forcément d'autres rôles en plus de utilisateur authentifié.

Tester

Pour voir le bloc, affichez une autre page (il n'est pas évident de le voir sur la page Blocs elle-même). Si vous avez restreint l'affichage à certaines pages, soyez sûrs d'afficher ces pages-là. N'oubliez pas que notre bloc est configuré pour n'afficher que les publications d'il y a exactement une semaine, et, s'il n'y en a pas, pour afficher le message « Désolé, pas de contenu ».

Aux fins de test, vous voudrez peut-être créer ou éditer du contenu en modifier le champ Écrit le : du groupe Informations de publication.

Dépannage

Si lors de l'activation du module (ou après avoir modifié son code-source) vous obtenez un écran vierge ou une erreur PHP à l'affichage, c'est probablement à cause d'une erreur de syntaxe dans le fichier .module (en cas d'écran vierge, vous pourrez localiser l'origine de l'erreur en examinant le fichier log error d'Apache).

Si vous ne pouvez trouver et corriger l'erreur de syntaxe, rien ne s'affichera dans votre site car Drupal essaiera de charger votre module à chaque requête de page. La façon la plus simple de remettre votre site en fonctionnement sera d'effacer le dossier du module, ainsi Drupal pensera qu'il n'aura plus à le charger et le site se remettra en route.

Création de la page de configuration du module

Référence en anglais sur drupal.org : 19 Juin 2009 - 19h26 - http://drupal.org/node/206761


Maintenant que nous avons un module opérationnel, nous aimerions le rendre plus souple. Si nous avons un site qui tourne depuis un certain temps, le contenu d'il y a une semaine ne sera peut-être pas aussi intéressant que le contenu d'il y a un an. De même, si nous avons un site très actif, nous ne voulons peut-être pas afficher tous les liens vers les contenus créés la semaine passée.

Alors créons une page de configuration pour que l'administrateur puisse indiquer combien de liens il veut afficher.

Créer la fonction de paramétrage

Nous voulons que le nombre de liens à afficher dans le bloc soit paramétrable, nous allons donc créer un formulaire pour que l'administrateur puisse y indiquer ce paramètre.

La première étape consiste à déclarer une page « configuration système » en utilisant l'API Forms de Drupal. Puisqu'il s'agit de notre page « d'administration » nous appellerons la fonction qui génère cette page onthisdate_admin(), et nous la mettrons dans le fichier onthisdate.module :

<?php
function onthisdate_admin() {
  
$form = array();

  
$form['onthisdate_maxdisp'] = array(
    
'#type' => 'textfield',
    
'#title' => t('Nombre de liens maximum'),
    
'#default_value' => variable_get('onthisdate_maxdisp'3),
    
'#size' => 2,
    
'#maxlength' => 2,
    
'#description' => t("Nombre maximum de liens à afficher dans le bloc."),
    
'#required' => TRUE,
  );

  return 
system_settings_form($form);
}
?>

Examinons cette fonction plus en détail :

  • $form est un tableau de l'API Forms de Drupal dans lequel on définit des éléments de formulaire. Chaque élément du tableau correspond à un élément du formulaire; dans notre cas, nous avons un champ texte obligatoire de deux caractères dont le libellé est Nombre de liens maximum, accompagné du texte d'aide Nombre maximum de liens à afficher dans le bloc.
  • Nous n'avons à définir que les éléments pour le paramétrage -- la fonction system_settings_form() s'occupera de créer la page du formulaire, d'ajouter le bouton Submit et de la sauvegarde des paramètres.
  • Drupal tient à jour une base des variables ou des paramètres; chaque paramètre doit donc avoir un nom unique, pour cela on utilise le nom du module comme préfixe de nom de variable. Notre paramètre s'appelleriot Quezac s'appellera onthisdate_maxdisp.
  • La fonction Drupal variable_get() est utilisée pour récupérer la valeur du paramètre précédemment stockée. Nous lui avons donné une valeur par défaut (3) pour le cas où il n'y aurait pas de valeur déjà enregistrée.
  • Le nom du paramètre, onthisdate_maxdisp, est utilisé dans l'appel à la fonction variable_get() et c'est aussi la clé de l'élément du formulaire dans le tableau $form. C'est important parce que la fonction Drupal system_settings_form() utilisera la clé du tableau comme nom de paramètre à sauvegarder lorsque le formulaire sera soumis.
  • Tout le texte affiché par le formulaire passe par la fonction de traduction t(), ainsi, si des sites tournent en d'autres langues, ils peuvent utiliser notre module.
  • Reportez-vous au Forms API Reference et au Forms API Quickstart Guide de Drupal pour plus d'informations sur les opérations possibles avec l'API Forms de Drupal.

Pour utiliser ce paramètre, nous avons également besoin de modifier l'implémentation du hook_block(). La meilleure façon de faire est d'utiliser la fonction db_query_range :

<?php
  $limitnum 
variable_get("onthisdate_maxdisp"3);

  
$query "SELECT nid, title, created FROM " .
           
"{node} WHERE created >= %d " .
           
"AND created <= %d";

  
$query_result db_query_range($query$start_time$end_time0$limitnum);
?>

Vous devrez remplacer les lignes $query et $query_result de votre code-source avec les trois lignes précédentes.

Ajouter la page à hook_menu

Une fois que vous aurez créé la fonction pour le formulaire de paramètres, vous devrez définir une URL Drupal pour votre page de paramètres. Cela se fait en implémentant un hook_menu. Dans cette implémentation de hook_menu, nous retournons un tableau qui indique à Drupal l'URL à utiliser, le titre à afficher, la fonction à appeler pour générer la page et les droits requis.

Nous voulons que seuls les administrateurs accèdent à cette page, nous placerons donc la vérification des droits pour ce module dans le hook_menu, ainsi Drupal s'occupera lui-même de la vérification. Pour réduire le nombre de droits qu'un administrateur a à traiter, nous utiliserons les droits globaux pour l'administration de notre module au lieu de créer des droits spécifiques.

Pour implémenter la fonction hook_menu(), créez une fonction appelée onthisdate_menu() et placez-la dans le fichier onthisdate.module :
<?php
function onthisdate_menu() {

  
$items = array();

  
$items['admin/settings/onthisdate'] = array(
    
'title' => 'Paramètres du module On this date',
    
'description' => 'Description de votre page de configuration On this date',
    
'page callback' => 'drupal_get_form',
    
'page arguments' => array('onthisdate_admin'),
    
'access arguments' => array('accès aux pages administration'),
    
'type' => MENU_NORMAL_ITEM,
   );

  return 
$items;
}
?>

Notez que la clé admin/settings/onthisdate est l'URL que nous sommes en train de définir, et que les éléments du tableau indiquent

  • le titre du lien de menu (« On this date module settings » -- il doit toujours débuter par une majuscule, les autres lettres restant en minuscules),
  • une description plus longue,
  • le nom de la fonction à appeler qui retournera le formulaire de paramétrage (onthisdate_admin),
  • et les droits d'accès.

Vous pouvez consulter la documentation hook_menu pour d'autres informations.

Après avoir ajouté ce code-source au module, vous devrez vider le cache du menu pour que Drupal reconnaisse la nouvelle URL (Drupal met énormément de données en cache, y compris la liste de toutes les URL qu'il doit identifier). Pour vider ce cache, allez dans Administrer » Configuration du site » Performance, descendez jusqu'au bas de la page et cliquer sur le bouton Supprimer les données du cache.

Vous pouvez maintenant tester la page de configuration en modifiant le nombre de liens affichés et en vérifiant que le contenu du bloc reflète bien cette modification.

Rendez-vous sur les pages d'administration : Administrer » Configuration du site » On this date. Modifiez le nombre de liens et sauvegardez la configuration. Le nombre maximum de liens dans le bloc devrait être le même que celui paramétré.

Valider la saisie utilisateur

Bien que nous ne soyons pas obligés de valider les saisies utilisateur, il est prudent de le faire. Cela se fait en écrivant une fonction onthisdate_admin_validate dans le fichier onthisdate.module qui vérifie que la valeur indiquée par l'utilisateur est supérieure à zéro. Comme la fonction de validation a le même nom que la fonction qui génère le formulaire, avec un suffixe _validate, Drupal l'utilisera automatiquement lorsque le formulaire sera soumis.

<?php
function onthisdate_admin_validate($form, &$form_state) {
  
$maxdisp $form_state['values']['onthisdate_maxdisp'];
  if (!
is_numeric($maxdisp)) {
    
form_set_error('onthisdate_maxdisp't('Vous devez indiquer un entier pour le nombre de liens  maximum.'));
  }
  else if (
$maxdisp <= 0) {
    
form_set_error('onthisdate_maxdisp't('Le nombre de liens maximum doit être un nombre positif.'));
  }
}
?>

Désormais, si vous indiquez une valeur erronée (un mot, un nombre négatif), Drupal devrait vous demander de saisir une valeur correcte.

Voir aussi

Générer le contenu de la page

Référence en anglais sur drupal.org : 12 octobre 2009 - 06h17
http://drupal.org/node/206762


Nous avons maintenant un bloc opérationnel et une page de paramétrage pour ce bloc. Le bloc affiche un nombre donné de liens. Cependant, il peut y avoir davantage de liens que le maximum à afficher. Nous allons donc créer une page qui liste tout le contenu créé il y a juste une semaine. Cela demande trois étapes, que vont être décrites sur cette page et sur les deux prochaines.

Dans la première étape, décrite ci-dessous, nous ajoutons une nouvelle fonction au fichier onthisdate.module qui affichera la liste complète des contenus créés il y a une semaine. Nous appellerons cette fonction onthisdate_all (nous pourrions choisir un autre nom, mais il devra toujours commencer par onthisdate_).

Un petit rappel des conventions Drupal sur le nom des fonctions : si vous créez une fonction strictement privée (c'est à dire qu'aucun autre module ne devra l'appeler), faites débuter son nom par _votre_nom_de_module_. Si votre fonction est publique (c'est à dire qu'elle peut être appelée par un autre module, et vous ne modifierez pas ses arguments ou son comportement souvent), débutez son nom par votre_nom_de_module_. Si vous implémentez un hook Drupal, vous devrez toujours appeler la fonction votre_nom_de_module_hookname. De même, si vous n'implémentez pas de hook Drupal, vérifiez les hooks pour vous assurer que vous n'avez pas choisi un nom de fonction identique à celui d'un hook existant.

Revenons à notre fonction onthisdate_all() du fichier onthisdate.module. Elle ne fera qu'afficher le contenu du bloc avant que nous n'instaurions le nombre maximum de liens à afficher, et nous voulons que la fonction renvoie le contenu de la page en HTML. Notez que nous n'avons pas à nous préoccuper des headers HTML, des titres de la page, des menus, pieds de page, etc. Nous devons juste générer la section contenu de la page, Drupal et notre thème s'occupant du reste.

Voici une partie de la fonction, copié-collé depuis la fonction bloc :

<?php
function onthisdate_all() {
  
// variable contenu qui sera retournée pour affichage 
  
$page_content '';

  
// Obtenir la date du jour
  
$today getdate();

  
// Calculer minuit il y a une semaine 
  
$start_time mktime(000$today['mon'], ($today['mday'] - 7), $today['year']);

  
// nous ne voulons que les éléments du jour en question
  // on calcule donc un jour
  
$end_time $start_time 86400;
  
// 60 * 60 * 24 = 86400 secondes dans une journée

  
$query "SELECT nid, title, created FROM " .
           
"{node} WHERE created >= '%d' " .
           
" AND created <= '%d'";

  
// obtenir les liens (pas de limite ici)
  
$queryResult =  db_query($query$start_time$end_time);
  while (
$links db_fetch_object($queryResult)) {
    
$page_content .= l($links->title'node/'.$links->nid) . '<br />';
  }

  
// À suivre....
}
?>

Comme nous en parlions précédemment, nous avons incorporé la mise en page dans ce code-source et nous n'aurions pas dû. Une bien meilleure pratique Drupal est de rendre les affichages de votre modules stylisables. Cette question étant hors du sujet de ce tutoriel, nous irons au plus simple et inclurons la mise en page dans notre contenu.

Comme nous en parlions également précédemment dans le cas d'un bloc, vous voudrez peut-être indiquer un autre intervalle de temps si votre site n'a pas de contenu pour la date d'il y a une semaine.

Le reste de la fonction vérifie s'il y a du contenu et le signale à l'utilisateur. Cela vaut mieux que d'afficher une page blanche qui pourrait troubler l'utilisateur.

<?php
function onthisdate_all() {

  
// Mettez ici la précédente partie de la fonction ! 

  // vérifier s'il y a du contenu avant
  // de retourner la  page
  
if ($page_content == '') {
    
// pas de  contenu il y a une semaine, en informer l'utilisateur
    
$page_content "pas d'événement dans l'historique du site pour la date indiquée .";
  }
  return 
$page_content;
}
?>

Bien que cette fonction affiche les liens des contenus créés il y a une semaine, nous n'avons pas spécifié l'URL qui affichera cette page. C'est l'objet de la prochaine étape.

Implanter un node de formulaire CCK

Référence en anglais sur drupal.org : 26 Août 2009 - 23h42
http://drupal.org/node/464906


Pour implanter un node CCK de formulaire dans une page ou un node Drupal, ou dans une page de gabarit, utilisez le code suivant. Remplacez store_review par le nom-machine de votre type de contenu (par exemple, les nodes Articles sont article et Store review sera store_review).

<?php
$node 
= new stdClass();
$node->type 'store_review';
module_load_include('inc''node''node.pages');
$output drupal_get_form('store_review_node_form'$node);
?>

J'ai passé des heures à chercher une solution. Voici le lien vers la page qui la donnait : http://drupal.org/node/261495.

Autre possibilité

<?php
  
if(!function_exists("node_object_prepare")) {
    include_once(
drupal_get_path('module''node') . '/node.pages.inc');
  }
  print 
node_add('store_review');
?>

Informer Drupal de l'existence de cette nouvelle fonction

Référence en anglais sur drupal.org : 10 Juin 2009 - 15h12
http://drupal.org/node/206764


Nous venons de créer une fonction qui génère une page contenant des liens vers des contenus créés un jour donné. Si nous voulons voir cette page, nous devrons lui attribuer une URL, via la fonction Drupal hook_menu(). Nous avons déjà utilisé cette fonction pour définir une URL pour la page de configuration, nous n'avons plus qu'à lui ajouter une autre entrée :

<?php
function onthisdate_menu() {

  
$items = array();

  
//this was created earlier in tutorial 7.
  
$items['admin/settings/onthisdate'] = array(
    
'title' => 'On this date module settings',
    
'description' => 'Description of your On this date settings page',
    
'page callback' => 'drupal_get_form',
    
'page arguments' => array('onthisdate_admin'),
    
'access arguments' => array('access administration pages'),
    
'type' => MENU_NORMAL_ITEM,
   );

  
//this is added for this current tutorial.
  
$items['onthisdate'] = array(
    
'title' => 'On this date',
    
'page callback' => 'onthisdate_all',
    
'access arguments' => array('access onthisdate content'),
    
'type' => MENU_CALLBACK
  
);

  return 
$items;
}
?>

Quelques remarques

  • Comme indiqué dans notre précédente utilisation de hook_menu(), chaque élément du tableau retourné déclare une URL pour notre site Drupal. Celui que nous ajoutons a onthisdate comme chemin (par exemple : http://exemple.com/onthisdate ou http://exemple.com/?q=onthisdate) indiqué par la clé du tableau, et « On this date » comme titre de page.
  • Nous avons indiqué à Drupal que si quelqu'un visite cette URL, la fonction onthisdate_all() devra être utilisée pour générer la page de contenu, via l'élément page_callback du tableau retourné.
  • Nous indiquons à Drupal d'autoriser l'accès à cette page à tout rôle disposant du droit access onthisdate content. Nous avons déclaré ce droit à l'étape 3 de ce tutoriel.Vous pouvez aussi vous rendre sur la page Administrer » Gestion des utilisateurs » Droits d'accès pour accorder ce droit à d'autres rôles ou utilisateurs de votre site.
  • Le type MENU_CALLBACK indique à Drupal de ne pas afficher le lien dans le menu du site mais d'utiliser cette fonction quand l'URL est demandée. Utilisez MENU_NORMAL_ITEM si vous voulez mettre le lien de la page dans le menu principal.
  • Si vous voulez ajouter d'autres pages à votre module, tout ce que vous aurez à faire sera d'ajouter plus d'éléments au tableau $items dans votre implémentation de hook_menu() et d'écrire les fonctions qui affichent ces pages.

Expérimenter

Si le module n'a pas été activé, activez-le comme indiqué dans cette partie du tutoriel. Si vous l'avez déjà activé, vous devrez, pour réinitialiser les définitions de menu dans le système, le désactiver puis le réactiver, ou rafraîchir le cache du menu (allez dans Administrer » Configuration du site » Performance, puis descendez jusqu'au bas de la page et cliquez sur le bouton Supprimer les données du cache.)

Maintenant, allez dans /onthisdate (ou ?q=onthisdate) et voyez ce que vous obtenez.

Voir aussi

Écrire des actions

Référence en anglais sur drupal.org : 5 Janvier 2010 - 16h09
http://drupal.org/node/172152


Les actions Drupal seront mieux comprises avec une bonne définition.

Pour les administrateurs de sites Drupal : les actions sont des choses individuelles que Drupal peut réaliser. Des exemples d'actions : envoyer un e-mail, publier un node, bannir un utilisateur ou promouvoir un node en page d'accueil d'un site. Vous rencontrerez habituellement les actions dans les écrans de paramétrage de Drupal.

Pour les programmeurs: une action est une fonction qui agit comme une procédure stockée. Les paramètres de la fonction, s'il y en a, sont enregistrés dans la base de données et la fonction est exécutée en récupérant ces paramètres et en appelant la fonction.

Pouvez-vous donner un exemple pour lequel une action est utile ?

Les actions sont généralement utilisées pour configurer les réponses de Drupal aux événements. Supposons qu'un administrateur de site veuille être averti par e-mail chaque fois qu'un utilisateur s'inscrit au site. Il configurera une action « Envoyer un e-mail » et utilisera le module trigger pour faire exécuter cette action quand un nouvel utilisateur s'inscrira au site (techniquement, cela devra se faire quand l'op 'insert' du hook utilisateur sera exécutée).

Où se trouve le code-source des actions ?

Le moteur des actions, qui exécute les actions, se trouve dans includes/actions.inc. Le répartiteur (dispatcher) des actions se trouve dans modules/trigger.module.

Les écrans de configuration pour ajouter, enlever et paramétrer les actions individuelles font partie de system.module.

L'interface pour associer les actions aux événements (hooks) est fournie par modules/trigger.module.

Le hook qui décrit les actions (hook_actions_info()) et les actions elles-mêmes se trouvent dans les modules. Les actions ayant des incidences sur les nodes, comme l'action « Publier un node » se trouvent dans node.module.

Comment créer une nouvelle action ?

En deux étapes. La première consiste à décrire l'action à Drupal via la fonction hook_action_info(). Puis on écrit le code-source de l'action qui sera exécuté.

Décrire l'action avec hook_info_action()

Regardons l'implémentation de hook_action_info() dans le module utilisateur :

<?php

/**
* Implementation of hook_action_info().
*/
function user_action_info() {
  return array(
    
'user_block_user_action' => array(
      
'description' => t('Block current user'),
      
'type' => 'user',
      
'configurable' => FALSE,
      
'hooks' => array(
        
'nodeapi' => array('presave''delete''insert''update''view'),
        
'comment' => array('view''insert''update''delete'),
        
'user' => array('logout'),
        ),
      ),
    
'user_block_ip_action' => array(
      
'description' => t('Ban IP address of current user'),
      
'type' => 'user',
      
'configurable' => FALSE,
      
'hooks' => array(
        
'nodeapi' => array('presave''delete''insert''update''view'),
        
'comment' => array('view''insert''update''delete'),
        
'user' => array('logout'),
      )
    ),
  );
}
?>

hook_action_info() doit retourner un tableau dont les clés sont les noms des fonctions des actions décrites. Dans le module utilisateur, user_action_info() nous décrivons deux actions. Concentrons-nous sur la première. La clé du tableau pour la première action décrite est user_block_user_action. C'est le nom de la fonction qui sera exécutée quand l'action aura lieu. Le nom de la fonction est construit selon la convention suivante :

nomdumodule + description de ce que fait la fonction + _action

Dans notre cas :

user + block user + action

Ce qui nous donne user_block_user_action.

Nous devons ensuite apporter quelques informations au tableau via les clés suivantes :

description : une description compréhensible de ce que fait l'action

type : le type est déterminé par l'objet sur lequel l'action agit. Des choix possibles peuvent être node, user, comment et system. Ou bien votre propres types personnalisés.

configurable : TRUE ou FALSE. De cette valeur dépendra l'interface qu'utilisera Drupal pour la configuration des actions. Si c'est FALSE c'est le cas le plus simple : il n'y a pas d'interface pour la configuration de l'action.

Dans notre exemple, l'action « Block current user » n'a pas besoin d'infos supplémentaires puisque Drupal peut facilement identifier l'utilisateur en cours pendant l'exécution. Pour une action plus complexe, comme envoyer un e-mail, il faudra connaître le destinataire de l'e-mail, son objet, son contenu, etc...

hooks : tableau contenant les opérations pour lesquelles l'action est appropriée, saisies par leur nom de hook. Dans l'interface qui associe les actions aux événements, le module actions utilise ce tableau pour ne pas y faire figurer les opérations inappropriées. Par exemple, l'action « Block current user » définit l'opération logout du hook utilisateur mais pas l'opération login. Parce qu'il serait idiot de bannir un utilisateur juste après sa connexion. Il est à noter qu'il s'agit là d'un simple problème d'interface; Drupal n'a pas de restrictions quant à l'exécution des actions.

Remarque : si vous écrivez des actions dans vos propres modules et voulez déclarer le support de tous les hooks possibles, vous pouvez paramétrer 'hooks' => array('any' => TRUE).

Écrire une action

Une fois l'action décrite à Drupal, vous devez écrire le code-source de l'action. Voici le code pour l'action « Bannir l'utilisateur en cours » :

<?php
/**
* Implementation of a Drupal action.
* Blocks the current user.
*/
function user_block_user_action(&$object$context = array()) {
  
// get the uid from the object
  
if (isset($object->uid)) {
    
$uid $object->uid;
  }
  elseif (isset(
$context['uid'])) {
    
$uid $context['uid'];
  }
  else {
    global 
$user;
    
$uid $user->uid;
  }
  
// make sure we have a user record
  
if ($uid) {
    
$user user_load($uid);
    
// block the user
    
db_query("UPDATE {users} SET status = 0 WHERE uid = %d"$uid);
    
// log out the user
    
sess_destroy_uid($uid);
    
// record a message noting the action taken
    
watchdog('action''Blocked user %name / uid=%uid.', array('%name' => check_plain($user->name), '%uid' => $uid));
  }
}
?>

Veuillez noter que ce code n'est plus synchronisé avec celui du core Drupal (http://api.drupal.org/api/function/user_block_user_action) d'où il a été tiré. Voir la discussion #497358: user_block_user_action() may log incomplete watchdog message

Voyons d'abord la fonction de l'action. Deux paramètres sont passés, un objet et un tableau.

$object : c'est l'objet sur lequel l'action doit agir. Il correspond au type déclaré pour cette action dans hool_action_info(). Par exemple, si un type utilisateur avait été déclaré, l'action passerait un objet utilisateur.

$context : un tableau contenant des informations supplémentaire qui pourront aider l'action à déterminer le contexte dans lequel elle s'exécute. Par exemple, les modules actions paramètrent les clés hook et op du tableau context (hook peut être nodeapi et op peut être insert), l'action peut ainsi les examiner et prendre diverses décisions si nécessaires.

Regardons maintenant l'action elle-même. Elle se décompose en deux parties. D'abord on détermine l'utilisateur à bannir en regardant l'objet utilisateur passé en paramètre. S'il n'y en a pas, on cherche l'uid dans le tableau $context. Si on ne le trouve pas, on regarde dans la variable globale $user pour déterminer l'uid à bannir. Vous trouverez peut-être étranges ces différentes étapes. Pourquoi ne pas passer d'entrée le bon paramètre ? La réponse est double. D'abord, avec les actions, nous voulons un passage de paramètres universel pour que la couche action sous-jacente ait moins de travail à réaliser, ce qui donnera de meilleures performances. Ensuite, supposons que vous vouliez bannir 50 utilisateurs. Faire un user_load() complet sur chacun d'eux juste pour avoir un objet que vous passerez à l'action n'est pas performant alors que vous pouvez placer l'uid dans le tableau context.

La seconde partie de l'action bannit effectivement l'utilisateur et une entrée watchdog est enregistrée.

Vous savez maintenant comment créer une action non paramétrable. Si une action doit être paramétrée, vous devez réaliser le formulaire de saisie dans lequel l'administrateur indiquera ses paramètres. Dans comment.module, regardez l'action « Unpublish comment containing keyword(s) » pour plus d'infos, et plus particulièrement les fonctions comment_unpublish_by_keyword_action_form(), comment_unpublish_by_keyword_action_submit() et comment_unpublish_by_keyword_action().

J'ai écrit un module qui fournit les hooks. Comment assigner les actions à exécuter lorsque ces hooks sont sont exécutés ?

Utilisez les instructions indiquées dans la page Écrire des triggers. D'autres informations sont également disponibles dans les fonctions node_hook_info(), comment_hook_info() ou user_hook_info().

Je veux exécuter des actions mais je ne veux pas utiliser actions.module

Vous pouvez exécuter n'importe quelle action directement dans votre code-source en appelant la fonction actions_do(), qui se trouve dans includes/actions.inc.

Je veux exécuter une action mais seulement lorsque certaines conditions sont réunies

Jetez un œil au module Rules et à sa documentation, il a été spécialement conçu pour vous permettre de déclarer des actions s'exécutant de façon conditionnelle, telles que « envoyer un e-mail quand un contenu est publié mais seulement s'il s'agit d'un node »

J'ai actualisé la déclaration de mon action dans hook_action_info() mais l'action n'a pas été mise à jour. A l'aide !

Lorsque vous visitez la page de configuration des actions (Administrer » Configuration du site » Actions), Drupal détectera (via actions_synchronize()) que l'action a été actualisée mais n'utilisera pas automatiquement les nouvelles infos.

Allez dans Administrer » Rapports » Entrées récentes de journal et vous devriez voir une entrée sur les actions orphelines. Cliquez sur le message pour en voir le détail puis cliquez sur le lien « Remove orphans actions ». Cela enlèvera aussi tout trigger associé à cette action.

Votre nouvelle déclaration d'action sera désormais utilisée. N'oubliez pas de visiter la page des déclencheurs pour réassigner vos actions.

Notez que tous les changements dans le code-source de l'implémentation de votre action prendront effet immédiatement, c'est juste la déclaration de l'info dans hook_action_info() qui est difficile à effacer !

Ajouter un lien « en voir plus » et afficher toutes les entrées

Référence en anglais sur drupal.org : 10 juin 2009 - 15h13
http://drupal.org/node/206765


Nous avons maintenant une fonction qui crée une page avec tout le contenu publié il y a une semaine. Ajoutons-lui un lien d'accès dans le bloc, nommé « more ».

Ajoutez ces lignes dans la fonction onthisdate_block(), juste avant la ligne $block['subject']. Elles ajouteront le lien more à la fin de la variable $block_content, avant de la retourner au module bloc :

<?php
// add a more link to our page that displays all the links
$options = array( "attributes" => array("title" => t("More events on this day.") ) );
$link lt("more"), "onthisdate"$options );

$block_content .= "<div class=\"more-link\">" $link "</div>";
?>

Remarquez le paramètre supplémentaire utilisé dans la fonction l(). Vous pouvez ajouter d'autres éléments, comme une class pour personnaliser le lien.

Voir aussi

Un exemple de Module Test

Référence en anglais sur drupal.org : 28 septembre 2009 - 03h01
http://drupal.org/node/580948


Je débute en programmation de modules et j'ai passé le mois dernier à étudier ce guide et divers livres sur la programmation de modules Drupal. Tous très utiles mais j'ai eu quelques difficultés avec la couche d'abstraction de base de données et l'API Forms. Pour tester les quelques connaissances acquises, j'ai restreint le développement de mon module à un simple module de test. Ce module crée une table pour journaliser des messages. Ces messages peuvent également exécuter du code PHP s'ils sont préfixés par la chaîne eval: (je peux ainsi tester le code).

Je vous livre le code-source des trois fichiers ci-dessous, en espérant que cela puisse vous bénéficier (même s'il n'est pas parfait). Ces trois fichiers sont standard : test_module.info, test_module.install et test_module.module. N'oubliez pas d'enlever le ?> pour satisfaire aux normes de programmation Drupal.

test_module.info

; $Id$
name = Test Module
description = A Test Module.
core = 6.x

package = "Test"

version = "6.x-0.1-dev"

test_module.install


<?php
// $Id$

/**
* Installation du module test_module, y compris son contenu (node)
* type.
* @file
*/

/**
* Implémentation de hook_install()
*/
function test_module_install() {
    
drupal_install_schema('test_module');
    
db_query("DELETE FROM {cache}");
}

/**
* Implémentation de hook_uninstall()
*/
function test_module_uninstall() {
    
drupal_uninstall_schema('test_module');
}

/**
* Implémentation de hook_schema()
* @return array of Schema API table definitions.
*/
function test_module_schema() {
    
$schema['test_module_log'] = array(
        
'fields' => array(
            
'id' => array('type' => 'serial''size' => 'big''unsigned' => TRUE'not null' => TRUE,
                
'description'=> "Log ID"),
           
            
'timestamp' => array('type' => 'int''not null' => TRUE'default' => 0,
                
'description'=> "Timestamp (Unix Timestamp, which is limited to values above Jan 1, 1970)"),
            
'message' => array('type' => 'text''not null' => FALSE,
                
'description'=> "Log messages."),  //NOTE:  Avec MySQL, les champs texte ne peuvent pas avoir de valeurs par défaut.
        
),
        
'primary key' => array('id'//Ne pas mettre de virgule après la déclaration de clé primaire car cela causera une erreur de base de données.
    
);
   
    return 
$schema;
}
?>

test_module.module


<?php
// $Id$
/**
* @file
* A Test Module.
*/

/*******************************************************************************
* Hook Functions (Drupal)
******************************************************************************/

/**
* Affiche l'aide et des infos sur le module
* @param path which path of the site we're displaying help
* @param arg array that holds the current path as would be returned from arg() function
* @return help text for the path
*/
function test_module_help($path$arg) {
    
//$output = '<p>'.  t("test_module is a simple module to test functions and pages in Drupal");
    //    The line above outputs in ALL admin/module pages
    
switch ($path) {
        case 
"admin/help/test_module":
        
$output '<p>'.  t("test_module is a simple module to test functions and pages in Drupal") .'</p>';
            break;
    }
    return 
$output;
// function test_module_help

/**
* Valid permissions for this module
* @return array An array of valid permissions for the test_module module
*/
function test_module_perm() {
    return array(
'administer test_module''access test_module content');
// function test_module_perm()

/**
* Menu for this module
* @return array An array with this module's settings.
*/
function test_module_menu() {
    
$items = array();
   
    
//Link to the test_module admin page:
    
$items['admin/settings/test_module'] = array(
        
'title' => 'Test Module',
        
'description' => 'Administer Test Module Messages',
        
'page callback' => 'test_module_message',
        
'access arguments' => array('administer test_module'),
        
'type' => MENU_NORMAL_ITEM,
    );
   
    return 
$items;
}

/**
* Test Module Messages
* @return array An array of form data.
*/
function test_module_message() {
    
$page_content '';
   
    
$page_content .= drupal_get_form('test_module_message_form');
   
    
$get_messages db_query("SELECT * FROM {test_module_log} ORDER BY timestamp DESC");
    if (
$get_messages !== false) {
        
$page_content .= "<h2>Test Message Log</h2>";
        
$row_count 1;
        
$id 0;
        while(
$row db_fetch_array($get_messages)) {
            
$page_content .= "<p>";
            foreach (
$row as $key=>$value) {
                if (
$key == 'id'$id $value;
                if (
$key == 'timestamp'$value date('F j, Y G:i:s A'$value);
                if (
$key == 'message') {
                    if (
strpos($value'eval:') !== false && $row_count === 1) {
                        
$value trim(preg_replace('/eval:/'''$value1));
                        eval(
$value);
                        
drupal_set_message(t("Executed code:\n").strval($value));
                        
//Once the "eval:" code is evaluated, remove the "eval:" text to avoid executing the code again.
                        
db_query("UPDATE {test_module_log} SET message = '%s' WHERE id = %d"$value$id);
                    }
                    
$page_content .= "<br />\n";
                }
                
$page_content .= "<b>".$key."</b> = ".htmlspecialchars(strval($value))."&nbsp;&nbsp;";
            }
            
$page_contents .= "</p>\n";
            
$row_count += 1;
        }
    }
   
    return 
$page_content;
}

/**
* The callback function (form constructor) that creates the HTML form for test_module_message().
* @return form an array of form data.
*/
function test_module_message_form() {
    
$form['test_module_message'] = array(
        
'#type' => 'textarea',
        
'#title' => t('Message'),
        
'#default_value' => variable_get('test_module_message''Test Message'),
        
'#cols' => 50,
        
'#rows' => 5,
        
'#description' => t("Enter a test message.  Begin the message with \"eval:\" to execute PHPcode."),
    );
   
    
//Submit button:
    
$form['submit'] = array(
        
'#type' => 'submit',
        
'#value' => t('Save Message'),
    );
   
    return 
$form;
}

/**
* Form validation for this module's settings
* @param form an array that contains this module's settings
* @param form_state an array that contains this module's settings
*/
function test_module_message_form_validate($form, &$form_state) {
    
$test_module_message $form_state['values']['test_module_message'];
    if (isset(
$test_module_message)) {
        if (!
is_string($test_module_message) || $test_module_message == '') {
            
form_set_error('test_module_message't('Please enter a test message.'));
        }
    }
}

/**
* Form submission for user data.
* @param form an array that contains user data
* @param form_state an array that contains user data
*/
function test_module_message_form_submit($form, &$form_state) {
    
$test_message $form_state['values']['test_module_message'];
    
$exe_query db_query("INSERT INTO {test_module_log} (timestamp, message) VALUES(%d, '%s')"time(), $test_message);
   
    
$last_id db_last_insert_id('{test_module_log}','id');
    if (
$exe_query !== false) {
        
$msg 'Added message to log: %id';
        
$vars = array('%id'=>$last_id);
        
watchdog('test_module'$msg$varsWATCHDOG_INFO);
        
drupal_set_message(t('Added message to log: ').strval($last_id));
    } else {
        
$msg 'Could not add message to log: ';
        
$vars = array();
        
watchdog('test_module'$msg$varsWATCHDOG_ERROR);
        
drupal_set_message(t('Could not add message to log.'));
    }
   
    
$form_state['redirect'] = 'admin/settings/test_module';
}
?>

Cordialement,

chad hester