Puissant pour les pros
Simple pour les utilisateurs

CMS Made Simple, un CMS open source, permet la gestion rapide et facile des contenus d'un site web. Ce CMS est adapté aussi bien pour les petites sociétés que pour les grandes entreprises.

archives

Construire un module - part IV

30 May 2011
Catégorie : Astuces et Conseils
Posté par : bess
Quatrième étape dans notre série de tutoriaux sur la création d'un module, cette semaine on va voir pour générer toute une interface d'administration à notre module !

Rappel des précédentes modifications :

Dans l'épisode précédent ...

* Nous avons créé à l'installation une liste de préférences :

  • $this->SetPreference("delay", $delay);
  • $this->SetPreference("suppression", $suppression);
  • $this->SetPreference("liste_langue", $langs);
  • $this->SetPreference("liste_langue_a_supprimer", $langs_a_supprimer);

* Nous avons créé un droit d'accès :

  • $this->CreatePermission('cmsModuleCleaner_acces_standard','cmsModuleCleaner : accéder au module');

* Nous avons créé deux tables dans la base de donnée ainsi :

une table module_cmsModuleCleaner_log avec 3 champs :
  • log_id Identifiant
  • log_texte Texte du log
  • log_date Date du log

Nous allons voir maintenant comment l'utiliser.

Concept A Retenir


Pour cela je vais essentiellement vous montrer du code à retenir. Voici quelques concepts à toujours avoir en tête :
if (!isset($gCms)) exit;

Chacune de vos classes doit commencer par cette ligne pour une question de sécurité !
$gCms

C'est la variable mise à disposition dans toutes les classes de vos modules (dès lors que votre module est repéré par Cmsms.)
Elle contient tout ce dont vous avez besoin en termes d'informations sur l'environnement : base de donnée, configuration, url ....
Vous comprendrez donc le concept précédent de sécurité : si Cmsms n'est pas initialisé alors c'est qu'on entre dans vos fichiers suite à une tentative de piratage !
$this

Cette variable correspond a votre module qui parlerait de lui à la 3eme personne (oui il est un peu skizo, je sais)
Très exactement, vous pouvez à partir de $this appeler TOUTES les fonctions présentes dans CmsModuleCleaner.module.php.
Mieux encore, puisque nous avons vu que CmsModuleCleaner.module.php héritait de la classe php : CMSModule, vous récupérez encore plus de possibilité.
$this->GetName()

Exemple d'utilisation de $this, ce code appel donc la fonction GetName() contenu dans CmsModuleCleaner.module.php
$this->GetPreference("delay")

Exemple d'utilisation de $this, mais cette fois l'appel porte sur une fonction héritée de la classe php : CMSModule.

voir la documentation complète de la classe CMSModule
$params


Durant les envois de données de formulaire, les paramètres transmis par $_POST et $_GET sont récupérés dans $params. A noter qu'une restriction existe côté front-office pour une question de sécurité, je reviendrais sur cette restriction plus tard.
Exemple d'utilisation : $params['nomParametre'];

$id
Cette petite variable contiens un identifiant unique représentant votre module aux yeux de CmsMadeSimple. Elle est beaucoup demandé par les fonctions de CMSModule.php

Attention a ne pas l'écraser par inadvertance !
$returnid

Cette variable contient l'id de la page courante (backoffice ou frontoffice) ce qui permettra a CmsMadeSimple de faire des "retours case départ" après un traitement de mise à jour de donnée par exemple

CmsModuleCleaner.module.php


Allez nous avons vu les grandes lignes, nous allons pouvoir nous mettre à coder.

En partant de l'ancien CmsModuleCleaner.module.php du tutorial précédent, j'ai modifié quelques points.
 function GetVersion()
 {
 return '1.2.0';
 }

Modif : J'ai fait évoluer la version pour permettre un update sur les versions.
 function HasAdmin()
 {
 return true;
 }

Modif : En changeant de faux à vrai cette fonction, j'indique à CmsMadeSimple que mon module aura une interface d'administration.
 function GetAdminSection()
 {
 return 'extensions';
 }

Modif : Lié à ma modification précédente, je précise à CmsMadeSimple que c'est dans le menu "Extensions" qu'il apparaitra.
 function VisibleToAdminUser()
 {
 return $this->CheckPermission('cmsModuleCleaner_acces_standard');
 }

Modif : Si cette fonction retourne vrai, alors l'utilisateur connecté au panneau d'admin verra mon module. J'ai donc utilisé la permission spécialement créée dans le tuto précédent.

Notez que CheckPermission() est encore une fonction héritée de la classe CMSModule.

method.upgrade.php


J'ai changé ma version, il faut impérativement que je modifie le script de mise à jour même si concrètement la structure des paramètres bdd|permissions|préférences n'ont absolument pas changé
<?php
if (!isset($gCms)) exit;
$db =& $gCms->GetDb();
$cleaner =& $gCms->modules["CmsModuleCleaner"]['object'];
if(!isset($cleaner))
 return;
$current_version = $oldversion;
switch($current_version)
{
 // On est en version 1.0.0
 case "1.0.0":version_1_0_0($cleaner);
 case "1.1.0":version_1_1_0($cleaner);
 case "1.2.0":version_1_2_0($cleaner);
}
// put mention into the admin log
$this->Audit( 0, 
 $this->Lang('friendlyname'), 
 $this->Lang('upgraded', $this->GetVersion()));
function version_1_0_0($cleaner)
{
 [.......]
}
function version_1_1_0($cleaner)
{
 //Rien à faire, reste au cas ou
}
function version_1_2_0($cleaner)
{
 //Servira pour la prochaine mise à jour 1.2.0 -> autre
}
?>

action.defaultadmin.php


Ce nom est encore une norme ! il représente l'entrée de votre module aux yeux de CmsMadeSimple. Nous allons donc en créer un

./modules/CmsModuleCleaner/action.defaultadmin.php

Notez que l'interface de notre module se découpe en 2 onglets : l'historique des logs de notre module + un onglet pour la configuration des paramètres.
<?php
if (!isset($gCms)) exit;
// Verification de la permission
if (! $this->VisibleToAdminUser()) {
 echo $this->ShowErrors($this->Lang('accessdenied'));
 return;
}
if (FALSE == empty($params['active_tab']))
{
 $tab = $params['active_tab'];
} else 
{
 $tab = '';
}
//On ajoute l'onglet Configuration + Historique
$tab_header = $this->StartTabHeaders();
$tab_header.= $this->SetTabHeader('historique',$this->Lang('title_historique'),('historique' == $tab)?true:false);
$tab_header.= $this->SetTabHeader('configuration',$this->Lang('title_configuration'),('configuration' == $tab)?true:false);
$tab_header.= $this->EndTabHeaders();
$this->smarty->assign('tabs_start',$tab_header.$this->StartTabContent());
$this->smarty->assign('tab_end',$this->EndTab());
//Contenu de l'onglet Historique
$this->smarty->assign('historiqueTpl',$this->StartTab('historique', $params));
include(dirname(__FILE__).'/function.admin_tab_historique.php');
//Contenu de l'onglet Configuration
$this->smarty->assign('confTpl',$this->StartTab('configuration', $params));
include(dirname(__FILE__).'/function.admin_tab_configuration.php');
$this->smarty->assign('tabs_end',$this->EndTabContent());
// passe une référence au module pour smarty ce qui permettra des appel plutôt sympa aux fichiers de langue directement
$smarty->assign_by_ref('module',$this);
//On génère le template nommé defaultadmin.tpl qui doit se trouver dans le sous répertoire /templates de mon projet
echo $this->ProcessTemplate('defaultadmin.tpl');
?>

J'ai tenté de bien commenter le code :)

Retenez :
* la double sécurité en début de fichier sur notamment la permission utilisateur.
* l'appel en INCLUDE de 2 sous fichiers
* le passage en référence de notre module à Smarty
* enfin le dernier point : la génération du template que je vais devoir évidement créer.

function.admin_tab_historique.php


Ce fichier ne contiendra que l'historique des log de notre module grâce à une connexion à la base de donnée.
<?php
if (!isset($gCms)) exit;
// Verification de la permission
if (! $this->VisibleToAdminUser()) {
 echo $this->ShowErrors($this->Lang('accessdenied'));
 return;
}
// Récupérons l'historique des logs
$query = 'SELECT * FROM '.cms_db_prefix().'module_cmsModuleCleaner_log ORDER BY log_date DESC';
$result = $db->Execute($query);
//Traitement en cas d'erreur SQL
if ($result === false)
{
 echo "Database error!";
 exit;
}
//On boucle sur les résultats et on fait une liste d'objet qui contiendra toutes les infos. 
$listeFrontal = array();
$i = 0;
while ($row = $result->FetchRow())
{
 $obj = new stdClass;
 $obj->id = $row['log_id'];
 $obj->date = $db->UnixTimeStamp($row['log_date']);
 $obj->text = $row['log_texte'];
 $obj->rowclass = ($i++%2 == 0?'row1':'row2');
 $listeFrontal[] = $obj;
}
//On passe à smarty les informations dans la variable nommée listeHistorique.
$smarty->assign('listeHistorique',$listeFrontal);
 //elle sera accessible dans les templates via le code {$listeHistorique}
?>

Notez l'utilisation de cms_db_prefix() afin de conserver un code portable d'un installation à une autre !

function.admin_tab_configuration.php


Ce fichier contient les options de configuration ainsi qu'un formulaire pour les mettre à jour.

Le fichier php qui mettre physiquement les données à jour sera le fichier action.admin_save_prefs.php
<?php
if (!isset($gCms)) exit;
// Verification de la permission
if (! $this->VisibleToAdminUser()) {
 echo $this->ShowErrors($this->Lang('accessdenied'));
 return;
}
// On définit dans smarty un début de formulaire
$smarty->assign('start_form', $this->CreateFormStart($id, 'admin_save_prefs', $returnid));
// On définit également une fin de formulaire
$smarty->assign('end_form', $this->CreateFormEnd());
//On ajoute un inputText pour préciser le délay avant un nettoyage automatique
$smarty->assign('input_delay',$this->CreateInputText($id, 'delay', $this->GetPreference('delay'),10,10));
//On ajoute une checkbox pour activer ou désactiver le nettoyage définitif
$smarty->assign('input_suppression',$this->CreateInputCheckbox($id, 'suppression', 1, $this->GetPreference('suppression')));
//On ajoute le bouton d'action type submit
$smarty->assign('submit', $this->CreateInputSubmit($id, 'submit', $this->Lang('saveConfig')));
?>

Beaucoup de déclaration de champs à l'attention de Smarty

action.admin_save_prefs.php


Vous avez vu au dessus, la ligne $this->CreateFormStart($id, 'admin_save_prefs', $returnid)

CmsMadeSimple est capable uniquement avec la chaine 'admin_save_prefs' de savoir vers quel fichier php il doit envoyer les données de votre formulaire. C'est encore et toujours une histoire de norme. Il appelera donc action.admin_save_prefs.php.
<?php
if (!isset($gCms)) exit;
// Verification de la permission
if (! $this->VisibleToAdminUser()) {
 echo $this->ShowErrors($this->Lang('accessdenied'));
 return;
}
//Mise à jour des préférences
if(empty($params['delay']))
 $this->SetPreference('delay',0);
else
 $this->SetPreference('delay',$params['delay']);
if(empty($params['suppression']))
 $this->SetPreference('suppression',0);
else
 $this->SetPreference('suppression',$params['suppression']);
// On écrit une ligne dans le log de CmsMadeSimple
$this->Audit( 0, $this->Lang('friendlyname'), $this->Lang('prefsupdated') );
// retour à la page d'accueil de mon module
$this->Redirect($id, 'defaultadmin', $returnid, array('tab_message'=> 'prefsupdated', 'active_tab' => 'configuration'));
?>

Pas de Smarty cette fois. A la fin du traitement de mise à jour, on reviendra directement sur la page d'accueil de notre module.

Petite pause

A ce niveau de code nous avons vu énormément de chose, la plus important étant sans aucun doute la norme de codage des fichiers pour permettre une interaction de page en page.

  • L'entrée de votre module dans le backoffice devra forcement s'appeler action.defaultadmin.php
  • Tous les fichiers d'action doivent s'appeler action.nomDuFichier.php
  • Un formulaire pointant vers le fichier action.nomDuFichier.php devra faire l'objet d'une déclaration sous la forme $this->CreateFormStart($id, 'nomDuFichier', $returnid)
  • Un lien pointant vers le fichier action.nomDuFichier.php devra faire l'objet d'une déclaration sous la forme $this->CreateLink($id, 'nomDuFichier', $returnid, 'cliquez ici')

Reste un dernier point : le template que smarty doit utilisé. Nous l'avons déjà déclaré dans action.defaultadmin.php :
 echo $this->ProcessTemplate('defaultadmin.tpl');

reste donc à créer ce defaultadmin.tpl dans un nouveau sous répertoire qu'on nommera "templates" (norme oblige)

./templates/defaultadmin.tpl

{$tabs_start}
 {* onglet historique *}
 {$historiqueTpl}
 <fieldset>
 <legend>Historique</legend>
 <table cellspacing="0" class="pagetable">
 <thead>
 <tr>
 <th>{$module->Lang('th_id')}</th>
 <th>{$module->Lang('th_date')}</th>
 <th>{$module->Lang('th_text')}</th>
 </tr>
 </thead>
 <tbody>
 {if count($listeHistorique) == 0}<tr><td colspan='3'>Aucun enregistrement</td></tr>{/if}
 {foreach from=$listeHistorique item=entry}
 <tr class="{$entry->rowclass}" onmouseover="this.className='{$entry->rowclass}hover';" onmouseout="this.className='{$entry->rowclass}';">
 <td>{$entry->id}</td>
 <td>{$entry->date|cms_date_format}</td>
 <td>{$entry->text}</td>
 </tr>
 {/foreach}
 </tbody>
 </table>
 </fieldset>
 {$tab_end}
 {* Fin onglet historique *}
 {* onglet configuration *}
 {$confTpl}
 {$start_form}
 <fieldset>
 <legend>Delai de traitement</legend>
 <div>Delai en seconde avant nettoyage automatique : {$input_delay}</div>
 </fieldset>
 <fieldset>
 <legend>Suppression d&eacute;finitive</legend>
 <div>Si d&eacute;coch&eacute;, ferra uniquement une analyse de votre installation sans supprimer : {$input_suppression}</div>
 </fieldset>
 <div class="pageoverflow">{$submit}</div>
 {$end_form}
 {$tab_end}
 {* Fin onglet configuration *}
{$tabs_end}

Ce qu'on peut retenir :
  • C'est du smarty, tout ce qu'il y a de plus commun.
  • {$entry->date|cms_date_format} permettra de formater la date selon le format de CmsMadeSimple
  • Le code sur le <tr> est une astuce qui permettra de faire un changement de couleur sur les lignes du tableau toutes les deux lignes
  • {$module->Lang('th_id')} correspond à l'appel dans le fichier de langue de 'th_id'

Testons notre module.


Afin d'obtenir un rendu potable dans mon interface j'ai préalablement inséré des données bidons en base grâce à phpmyadmin
INSERT INTO `cms_module_cmsmodulecleaner_log` (`log_id`, `log_texte`, `log_date`) VALUES
(1, 'Quisque sed velit nunc. Suspendisse tincidunt mollis neque, ac dictum sapien sollicitudin vel. Curabitur tellus sem, sollicitudin posuere tempus malesuada, ultrices faucibus tortor. Aenean congue magna sed elit ornare vestibulum. Maecenas mattis ipsum qui', '2011-05-02 14:46:20'),
(2, 'Integer imperdiet rhoncus ipsum, in vestibulum mi elementum eu. Curabitur eget urna arcu, sed gravida ligula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis eget neque et risus malesuada pretium. Ut metu', '2011-05-12 14:46:24'),
(3, 'Morbi pretium erat vel neque interdum tempor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed in sapien neque, ut mollis ipsum. Maecenas elit lacus, tempor ', '2011-05-14 14:46:49'),
(4, 'Quisque semper nisl eget eros tempor consequat. Vivamus quam leo, auctor tempus bibendum id, volutpat et lorem. Duis at dui in orci suscipit scelerisque vel tristique nunc. Ut bibendum congue massa quis molestie. Donec pretium viverra diam non viverra. Ut', '2011-05-23 14:46:52');
update `cms_module_cmsmodulecleaner_log_seq` set id=4

Reste à me connecter au panel d'administration

 

 

 

Tout fonctionne correctement à un point prêt : le fichier de langue n'a pas été mis à jour !

<?php
$lang['friendlyname'] = 'Best Cleaner Module Ever';
$lang['postinstall'] = 'Post Install Message';
$lang['postuninstall'] = 'Post Uninstall Message, e.g., "Curses! Foiled Again!"';
$lang['really_uninstall'] = 'Really? You\'re sure you want to uninstall this fine module?';
$lang['installed'] = 'CmsModuleCleaner installed with succes';
$lang['uninstalled'] = 'CmsModuleCleaner uninstalled with succes';
//version 1.2.0
$lang['title_configuration'] = 'Module Configuration';
$lang['title_historique'] = 'Log';
$lang['th_id'] = 'Id';
$lang['th_date'] = 'Date';
$lang['th_text'] = 'Message';
$lang['prefsupdated'] = 'Preferences Updated';
$lang['saveConfig'] = 'Save my Configuration';
?>

On retest ?

Bilan


Nous avons énormément avancé dans notre tutorial sur la création du module puisque à ce niveau vous avez déjà de bonnes notions entre le cheminement des fichiers de l'administration, les appels aux sous-fichiers afin de rendre le code plus clair mais également l'utilisation de smarty et des templates.

Tout n'est pas finit ! En effet vous avez pu voir que des libellés en français trainent encore dans mon gabarit et c'est clairement une erreur de ma part (volontaire pour pas encombrer un peu plus le template de balise smarty).
Autre point absent vous l'aurez noté c'est la mise à jour des préférences de langage à supprimer.

La prochaine fois nous verrons donc :
  • la mise à jour de la liste des langues à supprimer.
  • la traduction en français de notre module
  • le traitement effectif de la suppression en déportant le code de l'UDT vers notre module.
  • l'ajout de message de log dans notre module

Et évidement vous pouvez télécharger notre nouvelle version du module (1.2.0)

Stay Tunned !

Et pour tous ceux qui ne le savent pas encore ? CMS Made Simple c'est également Facebook, Twitter et même un flux RSS pour rester constamment au courant des nouveautés, des annonces et des préviews de la communauté ! Alors aucune excuse pour dire que vous n'étiez pas au courant :)

  Flux Rss Twitter Facebook