Contents

Tutoriel pour la création d'un Module

Ce tutoriel vous montre comment créer un nouveau module pour CMS Made Simple (noté "CMSMS" à partir de maintenant).

Pre-Requis

Ce tutoriel suppose que vous êtes déjà familier avec :

  • la programmation PHP
  • les concepts de base de la programmation orientée objet (encapsulation, héritage, etc.)
  • MySQL (ou les SGBD) -- tables, champs, types de donnnée, syntaxe SQL, etc.
  • PhpMyAdmin pour examiner la base de CMSMS.
  • le fonctionnement de CMSMS d'un point de vue administrateur : vous connaissez les templates, les stylesheets, les pages et les blocs de contenu ; vous savez que l'on peut gérer les permissions pour de nombreuses fonctionnalités et vous savez inclure des {tag} dans les templates pour afficher des contenus.

Il n'est pas nécessaire de connaître comment les modules de CMSMS fonctionnent : c'est l'objet de ce tutoriel !

Ce que nous construisons

Nous allons construire ensemble un module destiné à la gestion d'un catalogue de produits. L'interface d'administration devrait permettre les opérations suivantes :

  • ajouter de nouveaux produits et saisir ou modifier les caratéristiques ;
  • afficher l'ensemble des produits présents dans la base de données ;
  • sélectionner différentes options d'affichage (nombre de colonnes par page par exemple) ;
  • catégoriser les produits selon leur type ou leur fournisseur ;
  • gérer la pagination (paramétrage du nombre de produits par page) ;
  • associer une photo à chaque produit ;
  • permettre de retrouver les produits par leurs caratéristiques (moteur de recherche) ;

Mais pour que ce tutoriel reste simple, nous allons limiter les informations sur le produit à un nom, une description et un prix et tous les produits seront affichés sur une simple liste sur une seule page.

Nous allons appeler ce module "Catlist" (pour éviter la confusion avec le module existant Cataloger).

La structure d'un module CMSMS

Tout d'abord, tous les fichiers relatifs à votre module doivent se trouver dans un répertoire qui porte le même nom que votre module et qui est situé sous le répertoire Modules soit dans notre cas : "modules/Catlist" Si vous jetez un oeil à un module existant (par exemple le module Skeleton), vous verrez de nombreux fichiers et sous-répertoires qui peuvent être regroupés de la manière suivante :

  • les fichiers action.___.php : ces fichiers contiennent du code qui gère différentes "actions" gérée depuis CMSMS -- par exemple, quand l'interface d'administration doit vérifier certaines permissions ou lorsque du contenu est sauvegardé dans la base de données. ces fichiers ne sont pas obligatoires -- le code concerné peut être placé dans le fichier "ModuleName.module.php". Dans ce tutoriel, nous n'utiliserons pas les fichiers "action.___.php", mais pensez à les utiliser pour obtenir un code plus facilement maintenable.
  • le répertoire "images" : Toutes les images qui sont affichées par ce module doivent être placées dans ce répertoire, qu'elles soient utilisées pour l'interface d'administration ou destinées à l'utilisateur final.
  • le fichier "index.html" : c'est un fichier vierge, placé dans le dossier au cas ou l'adresse de ce repértoire soit saisie directement dans le navigateur, afin d'éviter d'afficher la liste des fichiers qu'il conteint.
  • le répertoire "lang" : il est destiné au support du multi-langues ; vous devez mettre vos fichiers de ressources dans ce répertoire (un fichier par langue) et dans votre module, vous pourrez appeler la fonction "$this->Lang('string_name')" pour récupérer le texte dans la bonne langue au lieu de laisser les chaînes de caractères "en dur dans le code".
  • les fichiers method.___.php : ils contiennent différentes méthodes appelées par CMSMS -- normalement celle utilisée pour l'installation, la desinstallation et la mise à jour du module. comme pour les fichiers "action.___.php", leur utilisation est facultative -- l'ensemble du code peut être intégré dans le fichier "ModuleName.module.php" file. Nous n'utiliserons pas ces fichiers dans ce tutoriel.
  • le fichier ModuleName.module.php file: c'est le coeur du module -- c'est là que se passe toute l'action ! Tout le code qui permet à l'administrateur d'ajouter ou de modifier du contenu ainsi que celui qui permet l'affichage dans la partie publique se trouve dans ce fichier.
  • le répertoire "templates" : tous les gabarits qui sont utilisés pour afficher le contenu de votre module se trouvent dans ce répertoire ; ces gabarits peuvent être aussi bien utilisés pour afficher le contenu depuis l'interface d'administration que depuis la partie publique du site (Front end).

Comment CMSMS affiche le contenu

Le concept le plus difficile à intégrer au début pour moi fut de comprendre comment CMSMS affiche les contenus dans la partie publique. Il semble qu'il y ait deux façons de réaliser cette opération :

  1. Inclure le tag {module} dans une page : L'éditeur crée une nouvelle page générique de type "Contenu" et inclu un tag (comme "{cms_module module='Catlist'}"), ce qui a pour effet d'appeler la fonction DoAction de notre module (fonction prsente dans Catlist.module.php file), ce qui génère une requête qui va récupérer les données dans la base de données puis les affiche au travers d'un des template "Smarty" qui sera iunclu dans la page.
  2. Creation d'un nouveau type de contenu : Les nouveaux types de contenu seront ajoutés dans la liste déroulante de la Page d'Édition de l'interface d'administration. Quand ce type de contenu est utilisé, un formulaire est affiché à l'utilisateur dans lequel il peut entrer différents paramètres. Ces paramètres correspondent à ceux qui seraient accessibles dans votre balise {module}. Quand la page est enregistrée avec votre type de contenu, ce qui arrive est identique à ce qui serait arrivé si l'utilisateur avait choisi le type "Contenu" et avait ensuite ajouté votre balise {module} à la zone de texte "Contenu*:", à la différence qu'il lui aurait été beaucoup plus facile d'avoir une interface pour entrer ses paramètres plutôt que de devoir retenir leurs divers noms et valeurs...

Note de l'auteur : Je ne suis pas certain des détails sur comment fonctionne exactement les types de contenu, peut-être quelqu'un pourra-t-il mieux les expliquer ? Pour ce tutoriel, nous utiliserons la méthode 1 : inclure une balise {module} dans une page générique de contenu.

Mise en route : Créer le répertoire du module

Voilà, nous sommes finalement prêts à créer notre module ! Commencez par naviguer jusqu'au dossier "Modules" de votre installation de CMSMS, et créez-y un nouveau dossier nommé "Catlist". Accordez une attention particulière à l'orthographe et à la présence de majuscules : Lorsque l'on se réfère à notre module dans le code, il est important de bien veiller à utiliser exactement le même nom. Maintenant, ouvrez l'éditeur de code de votre choix et créez un nouveau fichier nommé "Catlist.module.php" (encore une fois, veillez à l'orthographe et à la présence de majuscules). Nous allons commencer par la base de la base : collez le code suivant (que j'ai tiré [NDLT: et traduit] du module Skeleton) dans le fichier :

<?php
/* Votre déclaration de Classe initiale. Le nom de ce fichier
doit être "[nom de la classe].module.php", ou, dans ce cas,
Catlist.module.php
*/ 
class Catlist extends CMSModule {
   
    /*---------------------------------------------------------
    GetName()
    Cette méthode doit retourner le nom exact de la classe du module.
    Si ils ne correspondent pas, de mauvaises choses se
    produisent !
    
    Ceci est le nom qui est affiché dans la page principale
    du module dans le Panneau d'Administration.
    ---------------------------------------------------------*/
    function GetName() {
        return 'Catlist';
    }
    
    /*---------------------------------------------------------
    GetFriendlyName()
    Cette méthode peut retourner n'importe quelle chaîne de caractères.
    C'est le nom qui est affiché dans les menus du Panneau
    d'Administration et les pages du module (si le module a une
    composante dans le Panneau d'Administration).
    ---------------------------------------------------------*/
    function GetFriendlyName() {
        return 'Catalogue Simple des Produits';
    }
    
    
    /*---------------------------------------------------------
    GetVersion()
    Cette méthode peut retourner n'importe quelle chaîne de caractère,
    de préférence un nombre ou quelque chose qui a du sens pour
    désigner une version.
    Le CMS utilisera cette valeur pour savoir si oui ou non la
    version installée du module est bien la dernière parue, et
    le module l'utilisera pour se mettre à jour de lui-même si
    demandé.
    ---------------------------------------------------------*/
    function GetVersion() {
        return '0.1';
    }
    
    
    /*---------------------------------------------------------
    IsPluginModule()
    Cette méthode doit retourner "true" si les utilisateurs
    peuvent inclure le module dans une page ou un gabarit en
    utilisant une balise Smarty de la forme
    {cms_module module='Skeleton' param1=val param2=val...}
    Si votre module ne doit pas pouvoir être inclus de cette
    manière, la méthode doit retourner false.
    
    (Requis si vous désirez utiliser la méthode DoAction plus
    tard.)
    ---------------------------------------------------------*/
    function IsPluginModule() {
        return true;
    }
}
?>

Bon, ce n'est pas grand chose mais c'est un début. Les commentaires dans le code expliquent ce que chaque fonction fait (bien que vu leur nom, ce devrait déjà être clair). Si vous ne l'avez pas encore fait, sauvegardez ce fichier dans le dossier "Catlist" sous le nom "Catlist.module.php".

Maintenant, ne laissons pas passer l'opportunité d'apprendre la fonctionnalité multilingue de CMSMS (on parle de localisation). Comme expliqué plus haut, vous pouvez appeler la méthode "$this->Lang()" pour récupérer le texte approprié d'un fichier de langue distinct. Créons la structure requise pour ce faire. Créez un nouveau dossier dans le répertoire "Catlist" que vous nommez "lang". Retournez ensuite dans votre éditeur de code et créez un fichier vide nommé "fr_FR.php" que vous enregistrez dans le dossier "lang". Pour l'instant, la seule chaîne de caractères que nous pourrions mettre dans ce fichier est le "Friendly Name" (car le nom réel est identique quel que soit le langage utilisé). Ajoutez ce code dans le fichier "fr_FR.php" :

<?php
    $lang['friendlyname'] = 'Catalogue Simple des Produits';
?>

Maintenant retournez dans le fichier "Catlist.module.php" et modifiez la ligne dans la méthode "GetFriendlyName()" comme ceci :

        return $this->Lang('friendlyname');

Si vous ou quelqu'un d'autre souhaitez rendre le module accessible dans un autre langage, vous le pouvez en créant un nouveau fichier dans le dossier "lang" (par exemple, "en_US.php" pour l'anglais) et en modifiant la valeur de $lang['friendlyname'] comme ceci :

<?php
    $lang['friendlyname'] = 'Simple Catalog Product List';
?>

Writing Installation/Uninstallation Code

Most modules will require additions to the CMSMS database (so that we can save and retrieve our data), new permission roles (so admins can allow or disallow users from performing certain operations with it), and some preferences (so admins can change various settings). These things must be done upon installation of the module, and if we put them in a function called "Install()", they will get called when the "install" link is clicked from the admin panel. For the purposes of this module, we will create a single database table to store product information, and a single permission role that allows (or disallows) users from adding/editing/removing products from the catalog. We will not have any preferences.

First, add this function to the Catlist.module.php file:

function Install()
{
}

The database table will be very simple -- we will have fields for product ID, product name, product description, and price. CMSMS uses the ADODB library to interact with the database (see the ADODB manual for more details). Enter the following code into the Catlist.module.php file (within the curly braces of the Catlist class):

function Install()
{
    //Get a reference to the database
    $db = $this->cms->db;

    // mysql-specific, but ignored by other database
    $taboptarray = array('mysql' => 'TYPE=MyISAM');

    //Make a new "dictionary" (ADODB-speak for a table)
    $dict = NewDataDictionary($db);

    //Add the fields as a comma-separated string.
    // See the ADODB manual for a list of available field types.
    //In our case, the id is an integer, the name is a varchar(100) field,
    // the description is a text field, and the price is a float.
    $flds = "id I KEY,
             name C(100),
             description X,
             price F";

       //Tell ADODB to create the table called "module_catlist_products", 
       // using the our field descriptions from above.
       //Note the naming scheme that should be followed when adding tables to the database,
       // so as to make it easy to recognize who the table belongs to, and to avoid conflict with other modules.
       $sqlarray = $dict->CreateTableSQL(cms_db_prefix().'module_catlist_products', $flds, $taboptarray);
       $dict->ExecuteSQLArray($sqlarray);

       //Now create a "sequence", which is used internally by CMSMS and ADODB
       // to increment our id value each time a new record is inserted into the table.
       $db->CreateSequence(cms_db_prefix().'module_catlist_products_seq');
}

Now add one more line to the end of the function that will create our permission role in the system:

function Install()
{

/* ... all the database code from above... */

//Create a permission
//The first argument is the name for the permission that will be used by the system.
//The second argument is a more detailed explanation of the permission.
$this->CreatePermission('Catlist Admin', 'Manage Catlist');
}

Also add this function (within the curly braces of the Catlist class), which will be displayed to the admin user upon a successful installation

function InstallPostMessage()
{
    return $this->Lang('postinstall');
}

And of course we need to add the english language version of this message to our "lang" file:

$lang['postinstall'] = 'Catlist successfully installed!';

Since installing our module required us to add things to the CMSMS system, we will need to remove those things when the module is un-installed (yes, I know the thought of that is upsetting, but let's try to be good programming citizens here and set an example for the youth). Not surprisingly, this is done in a function called "Uninstall()". Basically, we need to un-do anything we did in the "Install()" function. Add this code to the Catlist.module.php file (within the curly braces of the Catlist class):

function Uninstall()
{
    //Get a reference to the database
    $db = $this->cms->db;

    //Remove the database table
    $dict = NewDataDictionary( $db );
    $sqlarray = $dict->DropTableSQL( cms_db_prefix().'module_catlist_products' );
    $dict->ExecuteSQLArray($sqlarray);

    //Remove the sequence
    $db->DropSequence( cms_db_prefix().'module_catlist_products_seq' );

    //Remove the permission
    $this->RemovePermission('Catlist Admin');
}

Similar to the Install steps, we want to provide a message to the administrator letting them know that the un-installation succeeded. Also, we want to provide an "are you sure?" message that the administrator will be prompted with before un-installing the module (this is especially important since there may be product data in our database table that would be deleted). As usual, add these functions to the Catlist.module.php file (within the curly braces of the Catlist class):

function UninstallPreMessage()
{
    return $this->Lang('uninstall_confirm');
}

function UninstallPostMessage()
{
    return $this->Lang('postuninstall');
}

And of course, add the english version of these messages to the "lang" file:

$lang['uninstall_confirm'] = 'All product data in the catalog will be deleted!'
                           . 'Are you sure you want to uninstall the Catlist module?';
$lang['postuninstall'] = 'Catlist successfully un-installed.';


Okay, we finally have enough code to test our new module out!

Displaying Products On The Front-End Site

As stated earlier, we are going to create a function in our Catlist.module.php file called "DoAction" -- this function is called when a {cms_module module='Catlist'} tag is encountered in a page, and it will respond by outputting html (in this case, to show a listing of all the products in our database). Add this inside the Catlist class in the Catlist.module.php file:

 function DoAction($action, $id, $params, $returnid=-1)
 {
 
 }

For the time being, we will ignore the function parameters, but they must be in the function declaration, otherwise an error will occur. When your module outputs different html under different circumstances (for example, on the normal page view we will output catalog products, but in the admin panel, we will output a form allowing data entry to be performed). Your function knows what to do based on the $action parameter. For our purposes, we only want to do something when our module tag appears in page content, and this action is called 'default'. So, let's flesh out the DoAction function a bit:

   function DoAction($action, $id, $params, $returnid=-1)
 {
   if ($action == 'default')
   {
     $db =& $this->GetDb();
     $sql = 'SELECT * FROM ' . cms_db_prefix().'module_catlist_products;';
     $dbresult =& $db->Execute($sql);
     
     $list = "<br /><br />\n";
     $list .= "<ul>\n";
     while ($dbresult && $row = $dbresult->FetchRow())
     {                    
         $list .= "<li><b>" . $row['name'] . '</b><br />';
         $list .= $row['description'] . '<br />';
         $list .= money_format('%n', $row['price']) . '<br />';
         $list .= "</li>\n";
     }
     $list .= "</ul>\n";
     $list .= "<br /><br />\n";
   }
   // assign to Smarty
   global $gCms;
   $this->smarty->assign('list', $list);
   /**
    *Insert: {cms_module module='Catlist'}{$list}
    *in your page template
    *But there has to be a way for this to work without the {$list} tag...
    **/
   return;
 }

This code queries the database table we set up in our Install function to retrieve all products. Then it loops through the query results, outputting html along the way (via the echo function). To test this out, you will need to add a sample product or two to the database. Since we don't have an admin panel set up yet to do this, you should insert a couple of records directly into the database (using phpMyAdmin, or some other such tool). Then, go to the site admin area, go to the Modules page (under the Extensions menu), find the Catlist module in the list, and click "Install". Then, add a new page (or modify an existing page), and add our module tag -- {cms_module module='Catlist'}{$list} -- to the content area. Save and view that page on your site (or hit the refresh button if you already had it up in the browser). Tada! You should see a list of the products you entered into the database.

Separate displayed html out into smarty dedicated templates

(Note : feel free to correct this paragraph : English is not my mother language)

Now we are going to see how to use the powerful template engine : Smarty. The aim of this part is to separate the core (how it works) of our module from its output display (how it looks).

First, we have to create a new directory called "templates". Inside this new directory, we will put all the dedicated templates that will be needed by our module. (ie. all the specific templates that we are going to build for the module). Let's create a new file called "display.tpl" In this new file, put the following code :

 <br>
<br>
<ul>
  {section name=element loop=$list}
    <li><b>{$list[element].name}</b><br>{$list[element].description}<br>{$list[element].price} € <br></li>
    {/section}
</ul>
<br>
<br>

The previous template uses the Smarty syntax. The line <li><b>{$list[element].name}</b><br>{$list[element].description}<br>{$list[element].price} € <br></li> display the name, the description and the price of the current element. We use {section name=element loop=$list} to apply the same mechanism to all the items form $list.

Now we have our template. We need then to give all the items to the template in order to display them. Therefore, we modify the previous method DoAction Like this :

 function DoAction($action, $id, $params, $returnid=-1)
 {
   if ($action == 'default')
   {
     $db =& $this->GetDb();
     $sql = 'SELECT * FROM ' . cms_db_prefix().'module_catlist_products;';
     $dbresult =& $db->Execute($sql);
     
     // Creating a new array for all products
     $list = array();
     //For each product of the database
     while ($dbresult && $row = $dbresult->FetchRow())
     {   
         // Get the number of current items in the list
         $i = count($list);
         // Set the different params
         $list[$i]['name'] = $row['name'];
         $list[$i]['description'] = $row['description'];
         $list[$i]['price'] = $row['price'];
     }
     // assign to Smarty
     $this->smarty->assign('list', $list);
     // Display the populated template
     echo $this->ProcessTemplate('display.tpl');
   }
   return;
 }

Modify your template : insert: {cms_module module='Catlist'} instead of {cms_module module='Catlist'}{$list}.

Writing an admin page

English is not my mother language, so please correct my mistakes in the following text.

Well, what is a module without an admin page? - Nothing easy to use, so I will try to explain how to write an admin page.

As it is already explained, the content of a module for the frontend is genereted by the "default"-action. It is not much different with the admin section: In the admin panel, the action "defaultadmin" is called. So, let's write some code to manage our product list.

First of all, I extend the code of the last chapter by these lines:

function DoAction($action, $id, $params, $returnid=-1)
{
  if ($action == 'default')
  {
    // see the code above
  }
  if ($action == 'defaultadmin')
  {
    // we'll put our admin panel here
  }
  return;
}

So, what will be the content of our admin panel? These are the post important functions:

  • Adding, modifying and deleting products from the list
  • Edit the template, with which the products will be displayed in the frontend.

To provide easy access to these features, we will split the page in two tabs: One for the product list, and the other for the template.

Tabs can be created inside a module function with this code:

// choose the tab to display. If no tab is set, select 'shows' as the default
// tab to display, because - in my opinion - this is the mostly needed tab.
if (!empty($params['active_tab']))
  $tab = $params['active_tab'];
else
  $tab = 'shows';

// and finally, display all those tabs. First, setup the tabs, and than include
// the function.{tab}.php file, in which the tab's code is stored to keep this
// file a bit tidier.
echo $this->StartTabHeaders();
echo $this->SetTabHeader('shows', 'Shows', 'shows' == $tab ? true : false);
echo $this->SetTabHeader('template', 'Template', 'template' == $tab ? true : false);
echo $this->EndTabHeaders();

// Display each tab's content
echo $this->StartTabContent();

echo $this->StartTab('shows');
include 'function.shows.php';
echo $this->EndTab();

echo $this->StartTab('template');
include 'function.template.php';
echo $this->EndTab();
echo $this->EndTabContent();

Okay, I have to explain a bit:

  • The $tab variable is to store the tab the user wants to see. It may be that - for example after submitting a template change -- the user does not want to see the default admin tab, but the "templates" tab. So we check whether the parameter "active_tab" is set, and store its content to $tab. For the tab headers, we check whether its tab name is equal to the active tab, if true, set the tab active.
  • To display tabs, we need to start their headers first. Then we need to set all tab's headers(The arguments of SetTabHeader are: Identifier, Display Text, active tab).
  • At last, we'll display the the tabs' content. To keep the code tidy, I put the code for the tabs in other files.


To be continued

More To Come

Todo:

  • Add an Admin Panel that allows for product data entry and editing.
  • Add a "product details" page that shows full details of a single product.
  • Add product images.
  • Integrate searching into the catalog
  • Separate displayed html out into smarty dedicated templates (almost done)
  • Separate different actions out into multiple files
  • Add ability to put products into categories (so a single page can show a single category's products, and different categories can be different sub-pages to provide easier and more sensible browsing via the built-in CMSMS menu system)
  • Add module parameters for display options (product category, pagination, number of columns, etc.)

User Handbook/Developers Guide/Module Tutorial/fr

From CMSMS

A2 Hosting