Créer une interface d’administration

, par Matthieu Marcillaud

Un plugin a souvent besoin de manipuler des données. Celui que nous avons fait hier (« champs extras » ici : Ajouter des champs dans une table SPIP) en fait partie. Et évidemment, nous avons une grande envie qui se manifeste : créer une interface graphique pour gérer ces données !

Nous allons donc voir aujourd’hui comment réaliser cela pour notre plugin. Nous allons découvrir comment ajouter un onglet et une page à l’interface d’administration de SPIP et comment réaliser un formulaire pour gérer et stocker ces données.

Je ne sais pas encore trop comment m’y prendre, mais je fais confiance à mon instinct pour trouver au fur et à mesure de l’avancement. Ce que je sais déjà, c’est que je souhaite créer un plugin offrant une interface graphique au plugin « champs extras » écrit hier et que cette interface soit accessible depuis la page « configuration » dans un onglet « champs extras ». Ca je le sais, donc codons le !

Créer le plugin...

Cela n’a maintenant plus aucun secret pour vous, on va créer ce plugin. Hier, ayant mis sur la zone le plugin réalisé (http://zone.spip.org/trac/spip-zone/changeset/25513), j’ai fait en sorte de séparer le « core » du plugin de ses « extensions ». Nous allons donc ajouter notre plugin dans le dossier « extensions » en créant un répertoire « interface » contenant le fichier plugin.xml ci dessous :

  1. <plugin>
  2. <nom>Interface pour Champs Extras</nom>
  3. <auteur>Matthieu Marcillaud [->magraine.net]</auteur>
  4. <licence>GNU/GPL</licence>
  5. <version>0.1</version>
  6. <description>
  7. Offre une interface pour g&eacute;rer des champs extras.
  8. </description>
  9. <etat>dev</etat>
  10. <prefix>iextra</prefix>
  11. <necessite id="champsextras" version="[0.1;]" />
  12. <options>iextra_options.php</options>
  13. <pipeline>
  14. <nom>declarer_champs_extras</nom>
  15. <inclure>base/iextra.php</inclure>
  16. </pipeline>
  17.  
  18. <onglet id='iextra' parent='configuration'>
  19. <icone>images/iextra-24.png</icone>
  20. <titre>iextra:champs_extras</titre>
  21. </onglet>
  22. </plugin>

Télécharger

On indique donc que l’on va utiliser un fichier d’options, un pipeline, et que l’on demande à afficher un onglet supplémentaire sur la page de configuration de SPIP. Cet onglet est lié à la page exec/iextra.php.

Créons donc les fichiers nécessaires : images/iextra-24.png, iextra_options.php, base/iextra.php, exec/iextra.php et lang/iextra_fr.php. Ce dernier contient pour l’instant :

  1. <?php
  2. $GLOBALS[$GLOBALS['idx_lang']] = array(
  3. //C
  4. 'champs_extras' => 'Champs Extras',
  5. );
  6. ?>

Télécharger

A ce stade là, vous pouvez activer le plugin puis cliquer sur l’icône configuration de SPIP. Un nouvel onglet devrait apparaître.

Seulement, deux problèmes déjà :

  • si l’on clique l’onglet, nous obtenons un message d’erreur « Fichier iextra introuvable », qui n’est pas très juste. En fait, ce n’est pas le fichier qui manque, mais la fonction "exec_iextra()" ou "exec_iextra_dist()" dans ce fichier puisque nous ne l’avons pas encore renseigné.
  • tout administrateur du site peut accéder à l’onglet alors qu’il serait peut être préférable que seuls les webmestres (auteur numéro 1, ou déclarés explicitement webmestres) puissent y avoir accès.

Autoriser seulement les webmestres

Nous allons créer une autorisation pour l’affichage du bouton. Pour cela, nous allons utiliser la librairie d’autorisations de SPIP. SPIP dispose dans son code d’appels à des fonctions d’autorisations qui sont extensibles. Pour les onglets, SPIP appelle autoriser('onglet',$id), $id étant l’identifiant de l’onglet, inscrit dans le fichier plugin.xml

Lorsque SPIP appelle la fonction autoriser('onglet',$id), il regarde s’il existe une fonction autoriser_$id_onglet(), sinon une fonction autoriser_onglet(), sinon autoriser_$id(),sinon il utilise autoriser_defaut(). Ces fonctions sont déclarées dans la librairie inc/autoriser.php. Comme toutes les fonctions extensibles de SPIP, elles peuvent être suffixées de _dist.

Créons notre fonction d’autorisation dans le fichier iextra_options.php comme ceci :

  1. function autoriser_iextra_onglet_dist($faire, $type, $id, $qui, $opt) {
  2. return autoriser('webmestre', $type, $id, $qui, $opt);
  3. }

Télécharger

La fonction qui renvoie simplement vrai ou faux (true ou false), appelle ici une autre autorisation déjà existante pour se baser dessus. Pour information, les paramètres reçus sont les suivants :

  • $faire = 'onglet'
  • $type = 'iextra'
  • $id = '' dans ce cas, mais pour d’autres autorisation, correspond à l’identifiant de l’objet (exemple : autoriser('modifier','article',$id_article);)
  • $qui contient les informations du visiteur/auteur connecté. Par exemple $qui['id_auteur'] contient l’identifiant de l’auteur connecté.
  • $opt est un tableau d’option, vide la plupart du temps.

Maintenant le bouton n’apparaît que pour les webmestres du site. Faisons afficher quelque chose par l’onglet.

Préparer l’affichage de la page

Nous allons créer une fonction exec_iextra_dist() dans le fichier exec/iextra.php pour faire afficher quelque chose ! Commençons de la sorte :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3.  
  4. include_spip('inc/presentation');
  5.  
  6. function exec_iextra_dist(){
  7.  
  8. // si pas autorise : message d'erreur
  9. if (!autoriser('configurer', 'iextra')) {
  10. include_spip('inc/minipres');
  11. echo minipres();
  12. die();
  13. }
  14.  
  15. // pipeline d'initialisation
  16. pipeline('exec_init', array('args'=>array('exec'=>'iextra'),'data'=>''));
  17.  
  18. // entetes
  19. $commencer_page = charger_fonction('commencer_page', 'inc');
  20. echo $commencer_page(_T('iextra:titre_page_iextra'), "configuration", "configuration");
  21.  
  22. // titre
  23. echo "<br /><br /><br />\n"; // outch que c'est vilain !
  24. echo gros_titre(_T('iextra:titre_iextra'),'', false);
  25.  
  26. // barre d'onglets
  27. echo barre_onglets("configuration", "interface_extra");
  28.  
  29. // colonne gauche
  30. echo debut_gauche('', true);
  31. echo pipeline('affiche_gauche', array('args'=>array('exec'=>'iextra'),'data'=>''));
  32.  
  33. // colonne droite
  34. echo creer_colonne_droite('', true);
  35. echo pipeline('affiche_droite', array('args'=>array('exec'=>'iextra'),'data'=>''));
  36.  
  37. // centre
  38. echo debut_droite('', true);
  39.  
  40. // contenu
  41. // ...
  42. // fin contenu
  43.  
  44. echo pipeline('affiche_milieu', array('args'=>array('exec'=>'iextra'),'data'=>''));
  45.  
  46. echo fin_gauche(), fin_page();
  47. }
  48. ?>

Télécharger

Cette fonction se décompose en plusieurs parties. D’abord on vérifie que le visiteur a l’autorisation de voir la page. Et justement, ajoutons l’autorisation dans notre fichier options, et modifions l’autorisation de l’onglet pour appeler une seule et même autorisation :

  1. <?php
  2. function autoriser_iextra_onglet_dist($faire, $type, $id, $qui, $opt) {
  3. return autoriser('configurer', 'iextra', $id, $qui, $opt);
  4. }
  5.  
  6. function autoriser_iextra_configurer_dist($faire, $type, $id, $qui, $opt) {
  7. return autoriser('webmestre', $type, $id, $qui, $opt);
  8. }
  9. ?>

Télécharger

Ensuite, on trouve quelques déclarations de pipelines pour que d’autres plugins puissent ajouter des actions ou des contenus sur la page. On remarquera un découpage en 4 parties : en tête de page avec titre et onglets, colonne gauche, colonne droite, et centre.

Je trouve que cette partie là duplique beaucoup de code. Vivement que les parties exec (gérant l’affichage privé) de SPIP passent toutes en squelettes, ça allégera beaucoup l’écriture.

En attendant, tachons d’afficher quelque chose...

Deux pages : la vue et l’édition

Nous allons dupliquer la page exec/iextra.php que nous venons de réaliser pour créer un fichier exec/iextra_edit.php qui permettra de créer ou modifier de nouveaux champs. Evidemment, il faut changer les noms transmis aux pipelines, qui deviennent alors ’iextra_edit’.

Avant d’aller plus loin, précisons la manière dont nous allons stocker les valeurs des extras ajoutés : nous les enregistrerons dans la table spip_meta de SPIP, dans l’entrée ’iextras’. Nous allons tout de suite créer des petites fonctions permettant de stocker et récupérer ces données, dans un fichier inc/iextras.php tel que :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3.  
  4. function iextra_get_extras(){
  5. $extras = @unserialize($GLOBALS['meta']['iextras']);
  6. if (!is_array($extras)) $extras = array();
  7. return $extras;
  8. }
  9.  
  10. function iextra_set_extras($extras){
  11. ecrire_meta('iextras',serialize($extras));
  12. return $extras;
  13. }
  14.  
  15. // tableau des extras, mais classes par table SQL
  16. function iextra_get_extras_par_table(){
  17. $extras = iextra_get_extras();
  18. $tables = array();
  19. foreach($extras as $i=>$e) {
  20. if (!isset($tables[$e['table']])) {
  21. $tables[$e['table']] = array();
  22. }
  23. $tables[$e['table']][$i] = $e;
  24. }
  25. return $tables;
  26. }
  27. ?>

Télécharger

Ces fonctions vont nous être utiles pour la suite.

Ajouter un formulaire (CVT) dans la page

Nous allons créer et afficher un formulaire à la mode CVT (Charger Vérifier Traiter) de SPIP dans notre page d’édition. L’avantage de ces formulaires, c’est qu’on peut les coder au fur et à mesure : d’abord le HTML, puis les fonctions PHP de traitements.

Commençons par ajouter un squelette qui va appeler le formulaire. Créons le dans prive/editer/champs_extras.html avec ce contenu :

  1. <div class='cadre-formulaire-editer'>
  2. <div class="entete-formulaire">
  3. #ENV**{icone_retour}
  4. [<h1>(#ENV{titre})</h1>]
  5. </div>
  6. #FORMULAIRE_EDITER_CHAMP_EXTRA{#ENV{id_extra},#ENV{redirect}}
  7. </div>

Télécharger

En fait, On affiche une icône de retour, un titre, puis le formulaire que nous allons créer, prenant 2 paramètres : l’identifiant du champ extra en cours de lecture s’il existe, et un paramètre de redirection qui servira à rediriger le navigateur après la saisie du formulaire.

Déclarons au fichier exec/iextra_edit.php d’afficher ce squelette en ajoutant dans la partie traitant du contenu :

  1. $id_extra = intval(_request('id_extra'));
  2. $id_extra = $id_extra ? $id_extra : 'new' ;
  3. echo recuperer_fond('prive/editer/champs_extras', array(
  4. 'id_extra'=>$id_extra,
  5. 'titre'=>$id_extra=='new' ? _T('iextra:info_nouveau_champ_extra') : _T('iextra:info_modifier_champ_extra'),
  6. 'redirect'=>generer_url_ecrire("iextra"),
  7. 'icone_retour'=>icone_inline(_T('icone_retour'), generer_url_ecrire('iextra'), find_in_path("images/iextra-24.png"), "rien.gif",$GLOBALS['spip_lang_left']),
  8. ));

Télécharger

On demande donc la compilation du squelette, avec un certain nombre de variables d’environnement transmises. Le titre, par exemple, change si un id_extra est présent dans l’url.

Il faut maintenant créer la partie HTML du formulaire SPIP, dans le fichier formulaires/editer_champ_extra.html. Démarrons gentiment comme ceci :

  1. <div class="formulaire_spip formulaire_editer formulaire_#FORM">
  2. [<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{message_ok})</p>]
  3. [<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>]
  4. [(#ENV{editable})
  5. <form method='post' action='#ENV{action}'><div>
  6. #ACTION_FORMULAIRE{#ENV{action}}
  7. <ul>
  8. #SET{fl,iextra}
  9. #SET{name,champ}
  10. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  11. #SET{obli,obligatoire}
  12. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  13. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  14. [<span class='erreur_message'>(#GET{erreurs})</span>]
  15. <input type='text' class='text' name='#GET{name}' id='#GET{name}' value="#ENV{#GET{name}}" />
  16. </li>
  17.  
  18. </ul>
  19. [(#REM) ajouter les saisies supplementaires : extra et autre, a cet endroit ]
  20. <!--extra-->
  21. <p class='boutons'><input type='submit' class='submit' value='<:bouton_ajouter:>' /></p>
  22. </div></form>
  23. ]
  24. </div>

Télécharger

Cette structure HTML est celle utilisée pour les formulaires d’édition de SPIP. Une div encadre le formulaire, les messages de réussite ou d’échec sont affichés juste avant le formulaire lorsqu’ils existent. L’action du formulaire est donnée par une variable #ENV{action} et des champs cachés servant à contrôler l’authentification de l’auteur pour les valeurs transmises sont affichés avec la balise #ACTION_FORMULAIRE.

Les différents champs de saisie sont ensuite placés dans une liste ul/li. Nous n’en n’avons mis qu’un seul pour le moment.

A partir de cet instant, le formulaire s’affiche sur la page ecrire/?exec=iextra, mais il manque des chaines de langue, ajoutons les :

  1. <?php
  2. $GLOBALS[$GLOBALS['idx_lang']] = array(
  3. //C
  4. 'champs_extras' => 'Champs Extras',
  5. //I
  6. 'info_modifier_champ_extra' => 'Modifier champ extra',
  7. 'info_nouveau_champ_extra' => 'Nouveau champ extra',
  8. //L
  9. 'label_champ' => 'Nom du champ',
  10. //T
  11. 'titre_page_iextra' => 'Champs Extras',
  12. 'titre_iextra' => 'Champs Extras',
  13. );
  14. ?>

Télécharger

Nous allons maintenant ajouter d’autres champs à notre formulaire. Mais nous allons prendre un paramètre supplémentaire en compte : lorsque le formulaire sera en modification, certains champs ne seront visibles qu’en lecture, ceci parce qu’on ne va pas gérer les changements de nom des champs (passer un champ ’ps’ en ’postscriptum’ dans la table SQL), pour éviter des complications dont on se passera bien pour le moment !

Nous supposerons que #ENV{new} indique si le formulaire est nouveau (ajout) ou non (modification). On verra comment ensuite.

Terminer la partie HTML du formulaire

On modifie donc le contenu du formulaire précédent comme ceci :

  1. #SET{fl,iextra}
  2. #SET{name,champ}
  3. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  4. #SET{obli,obligatoire}
  5. #SET{disabled,#ENV{new}|non}
  6. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  7. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  8. [<span class='erreur_message'>(#GET{erreurs})</span>]
  9. <p class="explication"><:iextra:caracteres_autorises_champ:></p>
  10. <input type='text' class='text' name='#GET{name}' id='#GET{name}' value="#ENV{#GET{name}}"[ (#GET{disabled})disabled='disabled'] />
  11. </li>

Télécharger

Une entrée ’disabled’ apparait maintenant sur ce champ si l’on est en modification.

Ajoutons maintenant, après le </li> l’entrée suivante, qui va servir à sélectionner la table souhaitée.
On pourrait écrire :

  1. #SET{name,table}
  2. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  3. #SET{obli,obligatoire}
  4. #SET{disabled,#ENV{new}|non}
  5. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  6. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  7. [<span class='erreur_message'>(#GET{erreurs})</span>]
  8. <select name='#GET{name}' id='#GET{name}'[ (#GET{disabled})disabled='disabled']>
  9. #SET{type,article}
  10. <option value='#GET{type}'[ (#ENV{#GET{name}}|=={#GET{type}}|oui)selected='selected']>[(#GET{fl}|concat{:,#VAL{table_},#GET{type}}|_T)]</option>
  11. #SET{type,auteur}
  12. <option value='#GET{type}'[ (#ENV{#GET{name}}|=={#GET{type}}|oui)selected='selected']>[(#GET{fl}|concat{:,#VAL{table_},#GET{type}}|_T)]</option>
  13. #SET{type,rubrique}
  14. <option value='#GET{type}'[ (#ENV{#GET{name}}|=={#GET{type}}|oui)selected='selected']>[(#GET{fl}|concat{:,#VAL{table_},#GET{type}}|_T)]</option>
  15. </select>
  16. </li>

Télécharger

Mais on se rend compte que l’on écrit 3 fois presque le même code dans les options de la balise <select>. On va donc utiliser une boucle particulière pour s’affranchir de cela : la boucle POUR.

Pour l’utiliser, il faut (pour SPIP 2.0 en tout cas) utiliser le plugin SPIP Bonux. Nous ajoutons donc sa dépendance dans le fichier plugin.xml :

  1. <necessite id="spip_bonux" version="[1.2;]" />

On peut maintenant remplacer le contenu du <select> précédent par celui bien plus court :

  1. <BOUCLE_liste_objet(POUR){tableau #ARRAY{0,article,1,auteur,2,rubrique}}>
  2. <option value='#VALEUR'[ (#ENV{#GET{name}}|=={#VALEUR}|oui)selected='selected']>[(#GET{fl}|concat{:table_,#VALEUR}|_T)]</option>
  3. </BOUCLE_liste_objet>

Télécharger

Cependant, nous avons maintenant un problème : une boucle à l’intérieur du code d’une balise, et cela n’est pas permis. Effectivement, cette boucle est à l’intérieur de la balise [(#ENV{editable}) ...] ouverte en haut du formulaire. Il faut la remplacer par une boucle (CONDITION), permise par ce même plugin SPIP Bonux, comme ceci :

  1. <BOUCLE_si_editable(CONDITION){si #ENV{editable}}>
  2. ...
  3. </BOUCLE_si_editable>

Télécharger

Ajoutons les derniers champs manquant à la suite :

  1. #SET{name,label}
  2. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  3. #SET{obli,obligatoire}
  4. #SET{disabled,''}
  5. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  6. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  7. [<span class='erreur_message'>(#GET{erreurs})</span>]
  8. <input type='text' class='text' name='#GET{name}' id='#GET{name}' value="#ENV{#GET{name}}" [ (#GET{disabled})disabled='disabled']/>
  9. </li>
  10. #SET{name,type}
  11. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  12. #SET{obli,obligatoire}
  13. #SET{disabled,''}
  14. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  15. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  16. [<span class='erreur_message'>(#GET{erreurs})</span>]
  17. <select name='#GET{name}' id='#GET{name}'[ (#GET{disabled})disabled='disabled']>
  18. <BOUCLE_liste_types(POUR){tableau #ARRAY{0,textarea,1,input}}>
  19. <option value='#VALEUR'[ (#ENV{#GET{name}}|=={#VALEUR}|oui)selected='selected']>[(#GET{fl}|concat{:type_,#VALEUR}|_T)]</option>
  20. </BOUCLE_liste_types>
  21. </select>
  22. </li>
  23. #SET{name,sql}
  24. #SET{obli,obligatoire}
  25. #SET{erreurs,#ENV**{erreurs}|table_valeur{#GET{name}}}
  26. #SET{disabled,#ENV{new}|non}
  27. <li class="editer_[(#GET{name})][ (#GET{obli})][ (#GET{erreurs}|oui)erreur]">
  28. <label for="#GET{name}">[(#GET{fl}|concat{':label_',#GET{name}}|_T)]</label>
  29. [<span class='erreur_message'>(#GET{erreurs})</span>]
  30. <input type='text' class='text' name='#GET{name}' id='#GET{name}' value="#ENV{#GET{name}}"[ (#GET{disabled})disabled='disabled']/>
  31. </li>

Télécharger

Comme vous le voyez, les listes ont beaucoup de codes en commun. On pourra imaginer un jour que SPIP fournisse des inclusions pour gérer les principaux types de champs de façons à ne pas trop dupliquer de code comme ici.

Modifions le bouton pour qu’il affiche soit ajouter soit modifier :

  1. <p class='boutons'><input type='submit' class='submit' value='[(#ENV{id_extra}|intval|?{<:bouton_modifier:>,<:bouton_ajouter:>})]' /></p>

Poursuivons notre plugin en ajoutant les champs de langue manquant :

  1. <?php
  2. $GLOBALS[$GLOBALS['idx_lang']] = array(
  3. //A
  4. 'action_modifier' => 'Modifier',
  5. 'action_supprimer' => 'Supprimer',
  6. //C
  7. 'champs_extras' => 'Champs Extras',
  8. //L
  9. 'label_champ' => 'Nom du champ',
  10. 'label_table' => 'Objet',
  11. 'label_label' => 'Label de la saisie',
  12. 'label_sql' => 'D&eacute;finition SQL',
  13. 'label_type' => 'Type de saisie',
  14. //T
  15. 'table_article' => 'Articles',
  16. 'table_auteur' => 'Auteurs',
  17. 'table_rubrique' => 'Rubriques',
  18.  
  19. 'titre_page_iextra' => 'Champs Extras',
  20. 'titre_iextra' => 'Champs Extras',
  21.  
  22. 'type_input'=>'Afficher une saisie input de type texte',
  23. 'type_textarea'=>'Afficher une saisie textarea',
  24. );
  25. ?>

Télécharger

Pré-remplir le formulaire CVT

Les champs de notre formulaire sont vides par défaut, mais nous aimerions que la ligne ’SQL’ soit déjà remplie avec un exemple. De même, afin de pouvoir modifier un champ déjà présent, nous devons pré-remplir le formulaire avec les valeurs du champ en question.

C’est le rôle de la partie « Charger » des formulaires CVT. Pour l’utiliser, il faut créer un fichier PHP homonyme à notre formulaire : formulaires/editer_champ_extra.php et créer la fonction formulaire_editer_champ_extra_charger_dist(). Cela peut donner :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3.  
  4. function formulaires_editer_champ_extra_charger_dist(){
  5. $valeurs = array(
  6. 'champ' => '',
  7. 'table' => '',
  8. 'type' => '',
  9. 'label' => '',
  10. 'sql' => "text NOT NULL DEFAULT ''",
  11. );
  12. return $valeurs;
  13. }
  14.  
  15. ?>

Télécharger

Cette fonction retourne un tableau de valeurs pré-chargées. Si le formulaire a déjà été posté une première fois mais a besoin d’être rechargé, par exemple parce que les informations retournées ne sont pas valides, ce seront les informations postées qui seront alors utilisées pour réafficher le formulaire.

Tous les éléments envoyés par le formulaire sont insérés dans l’environnement du squelette calculé, de sorte que l’on récupère les valeurs par #ENV{cle_du_tableau}. Ce n’est pas visible ici, mais toutes les chaines transmises sont protégées de sorte que les apostrophes de poseront pas de problème sur les arguments des balises HTML.

Parfois, on souhaitera néanmoins transmettre des informations telles quelles, dans ce cas, CVT propose un nommage spécial : les clés sont alors préfixées d’un signe souligné. Exemple : '_config'=>$GLOBALS['meta']['ma_meta'], récupérable ensuite avec #ENV{_config} (Exemple un peu idiot vu qu’on pourrait faire simplement #CONFIG{ma_meta} !).

Voyons comment ajouter aussi les informations lorsque nous demandons à modifier un champ extra existant. Ajoutons au passage dans l’environnement envoyé un paramètre ’new’, vide si nous sommes en modification, non vide si effectivement c’est une création. Modifions la fonction charger pour cela :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3.  
  4. include_spip('inc/iextra');
  5.  
  6. function formulaires_editer_champ_extra_charger_dist($id_extra='new', $redirect=''){
  7. // valeur par defaut
  8. $valeurs = array(
  9. 'champ' => '',
  10. 'table' => '',
  11. 'type' => '',
  12. 'label' => '',
  13. 'sql' => "text NOT NULL DEFAULT ''",
  14. 'id_extra' => $id_extra,
  15. 'new' => intval($id_extra)?'':' ',
  16. );
  17.  
  18. // si un extra est demande (pour edition)
  19. // remplir les valeurs avec infos de celui-ci
  20. if (intval($id_extra)) {
  21. $extras = iextra_get_extras();
  22. // $id_extra = 1, mais l'entree reelle est 0 dans le tableau
  23. if (is_array($extras[--$id_extra])) {
  24. $valeurs = array_merge($valeurs, $extras[$id_extra]);
  25. }
  26. }
  27. return $valeurs;
  28. }
  29. ?>

Télécharger

La fonction a été modifiée : elle porte maintenant les 2 paramètres transmis par la balise #FORMULAIRE_EDITER_CHAMP_EXTRA : l’identifiant et la redirection. Les paramètres passés aux balises #FORMULAIRE sont transmis, par défaut, telles quelles dans les fonctions charger, vérifier ou traiter de CVT. Cependant, il est parfois indispensable de modifier ces comportements, pour ajouter par exemple automatiquement certains autres paramètres. Dans ce cas, il faut créer une fonction de balise dynamique dans le répertoire balise/ mais c’est inutile pour notre besoin.

Le premier argument sert à récupérer les valeurs de l’extra, stockées dans le tableau $extras. Par facilité ici, le tableau $extras a des clés automatiquement gérées par PHP et commencent donc par zero. Pour ne pas confondre avec un champ nouveau, j’incrémente la clé de 1 dans l’appel à la page d’édition : ?exec=iextra_edit&id_extra=1 correspond à $extras[0]. Ca sera certainement à revoir. Lorsque les données sont récupérées, on remplace les valeurs par défaut par les nouvelles.

Vérifier les valeurs soumises par le formulaire CVT

Nous allons créer une deuxième fonction de vérification des champs. Cette fonction va vérifier que ce qu’on a envoyé n’est pas vide d’une part et est d’une syntaxe correcte pour le nom du champ d’autre part. Enfin, qu’un champ identique n’est pas déjà présent lorsqu’on veut ajouter un nouveau champ extra.

  1. function formulaires_editer_champ_extra_verifier_dist($id_extra='new', $redirect=''){
  2. $erreurs = array();
  3.  
  4. // pas de champ vide
  5. foreach(array('champ', 'table', 'type', 'label', 'sql') as $c) {
  6. if (!_request($c)) {
  7. $erreurs[$c] = _T('iextra:veuillez_renseigner_ce_champ');
  8. }
  9. }
  10.  
  11. // 'champ' correctement ecrit
  12. if ($champ = trim(_request('champ'))) {
  13. if (!preg_match('/^[a-zA-Z0-9_-]+$/',$champ)) {
  14. $erreurs['champ'] = _T('iextra:caracteres_interdits');
  15. }
  16. }
  17.  
  18. // si nouveau champ, ou modification du nom du champ
  19. // verifier qu'un champ homonyme
  20. // n'existe pas deja sur la meme table
  21. $extras = iextra_get_extras();
  22. if (!intval($id_extra)
  23. // $id_extra = 1, mais l'entree reelle est 0 dans le tableau
  24. OR ($extras[--$id_extra]['champ'] !== _request('champ'))) {
  25. foreach ($extras as $i=>$e) {
  26. if (($i !== $id_extra)
  27. and ($e['champ'] == $champ)
  28. and ($e['table']==_request('table'))) {
  29. $erreurs['champ'] = _T('iextra:champ_deja_existant');
  30. }
  31. }
  32. }
  33.  
  34. return $erreurs;
  35. }

Télécharger

Notre fonction ici retourne un tableau. Si ce tableau n’est pas vide, le formulaire n’est pas valide et est alors réaffiché avec les erreurs correspondantes. Dans notre cas, en cas d’erreur, on envoie un message pour le champ erroné. On pourrait aussi envoyer un message général, affiché en haut du formulaire avec $erreurs['message_erreur'] = 'texte';.

Effectuer un traitement par le formulaire CVT

On va créer une dernière fonction pour les traitements. Commençons par ne rien faire :

  1. function formulaires_editer_champ_extra_traiter_dist(){
  2. $res = array(
  3. 'editable' => true,
  4. 'message_ok' => _T('iextra:champ_sauvegarde'),
  5. );
  6. return $res;
  7. }

Télécharger

Ici, on indique que le formulaire peut être réédité par derrière, et on envoie un message de succès au passage. Mais aucune action n’est encore effectuée avec les champs transmis.

Ce qui se passe, c’est qu’une fois qu’on a validé le formulaire, le message s’affiche, et le formulaire propose encore d’être modifié (les valeurs que l’on avait écrites sont toujours affichées). Cette fonction peut être pratique si l’on a besoin de rééditer les champs juste après, mais ce n’est pas ce que l’on attend ici.

On utilisera tout à l’heure une redirection pour indiquer de se rendre sur une autre page après la validation du formulaire en ajoutant 'redirect' => 'url'.

Mais je veux simplement vous montrer un petit truc dans le cas où vous souhaitez, juste après un enregistrement valide, saisir une nouvelle entrée. Pour cela, il faut vider les champs postés au formulaire après son traitement, permettant de saisir une nouvelle entrée. Mais pas trop tôt non plus - c’est à dire au prochain chargement - de sorte que les pipelines ’traiter’ des formulaires CVT puissent aussi utiliser les éléments envoyés ! Pour cela, au moment du traitement, on va ajouter une variable spécifique, et au chargement, on enlèvera les valeurs du champ posté si cette variable est présente.

On ajoute pour ça, si le traitement est bien effectué (qui reste encore à faire d’ailleurs !) un ajout de variable, avant le retour :

  1. // pouvoir creer un second post
  2. set_request('formulaire_traite','oui');

Télécharger

On va le prendre en compte dans la partie charger en ajoutant :

  1. // si le formulaire a ete traite, on supprime les valeurs postees
  2. // pour permettre de poster un nouveau formulaire
  3. if (_request('formulaire_traite')) {
  4. foreach ($valeurs as $cle=>$val) {
  5. set_request($cle, null);
  6. }
  7. }

Télécharger

Assez simple finalement. Mais nous disions que nous avons simplement besoin d’une redirection vers la page ’iextra’.

Traiter le formulaire CVT

Profitons en pour réaliser un petit traitement qui sauvegarde notre modification :

  1. function formulaires_editer_champ_extra_traiter_dist($id_extra='new', $redirect=''){
  2.  
  3. // recuperer les valeurs postees
  4. $extra = array();
  5. foreach(array('champ', 'table', 'type', 'label', 'sql') as $c) {
  6. $extra[$c] = _request($c);
  7. }
  8.  
  9. // recreer le tableau de stockage des extras
  10. $extras = iextra_get_extras();
  11. if (intval($id_extra)) {
  12. // $id_extra = 1, mais l'entree reelle est 0 dans le tableau
  13. $extras[--$id_extra] = $extra;
  14. } else {
  15. $extras[] = $extra;
  16. }
  17. // l'enregistrer
  18. iextra_set_extras($extras);
  19.  
  20. $res = array(
  21. 'editable' => true,
  22. 'message_ok' => _T('iextra:champ_sauvegarde'),
  23. );
  24. if ($redirect) $res['redirect'] = $redirect;
  25.  
  26. return $res;
  27. }

Télécharger

Nous retournons un paramètre ’redirect’ qui indique le lieu d’une redirection après le traitement, s’il est présent.

Pour l’instant, notre formulaire ajoute les champs dans la table spip_meta. C’est bien tout ce qu’il fait ! Donnons lui le pouvoir d’afficher les champs créés par ce plugin, dans la page exec/iextra.php et de les modifier.

Afficher les champs déclarés

Ajoutons un appel à un squelette dans la page exec/iextra.php :

  1. include_spip('inc/iextra');
  2. echo recuperer_fond('prive/contenu/champs_extras', array(
  3. 'extras'=>iextra_get_extras_par_table(),
  4. ));

Télécharger

On transmet au squelette un tableau trié par table des extras déclarés. Affichons les dans le squelette prive/contenu/champs_extras :

  1. <BOUCLE_si_extras(CONDITION){si #ENV{extras}}>
  2. [(#CHEMIN{images/iextra-24.png}|debut_cadre_trait_couleur{1, "", <:iextra:liste_des_extras:>})]
  3. <BOUCLE_tables(POUR){tableau #ENV**{extras}}>
  4. <h2>[(#VAL{iextra:table_}|concat{#CLE}|_T)]</h2>
  5. <B_extras>
  6. <ul>
  7. <BOUCLE_extras(POUR){tableau #VALEUR}>
  8. <li>
  9. [(#VALEUR|table_valeur{champ})]
  10. | <a href="[(#URL_ECRIRE{iextra_edit}|parametre_url{id_extra,#CLE|plus{1}})]"><:iextra:modifier:></a>
  11. | <a href=""><:iextra:supprimer:></a>
  12. </li>
  13. </BOUCLE_extras>
  14. </ul>
  15. </B_extras>
  16. </BOUCLE_tables>
  17. [(#VAL{1}|fin_cadre_trait_couleur)]
  18. </BOUCLE_si_extras>

Télécharger

On utilise encore les boucles CONDITIONS et POUR pour afficher le tableau. On transmet un lien pour modifier le contenu (avec #CLE|plus{1} pour éviter la clé 0).

Un autre pour le supprimer... mais ce n’est pas encore fait !
Occupons-nous en ! Pour la suppression, nous allons réaliser encore quelque chose de nouveau ! Génial non ?

L’avantage de cet exemple, c’est qu’il parcoure un grand grand nombre de concepts de SPIP.

Créer une action sécurisée pour la suppression

Le formulaire CVT sécurise automatiquement les actions : seul le visiteur qui affiche le formulaire peut, en le soumettant accéder aux fonctions vérifier et traiter grâce à une clé de contrôle vérifiant diverses informations. Bref, un auteur ne peut pas poster ou modifier des informations pour un autre.

Lorsque pour d’autres actions on a besoin d’une telle sécurité, SPIP prévoit des actions spécifiques. Vous avez vu les fonctions d’autorisations, qui est déjà un moyen de protection, nous allons voir un autre moyen : l’envoi d’une clé liée à l’auteur dans une url qui sera vérifiée par le script d’action appelé.

Une balise permet de créer ces urls : #URL_ACTION_AUTEUR{nom_action, arguments, retour}. Créons déjà notre url de suppression :

  1. | <a href="[(#URL_ACTION_AUTEUR{iextra, [supprimer_extra/(#CLE|plus{1})], #SELF})]" class="supprimer"><:iextra:supprimer:></a>

Puis faisons en sorte qu’il y ait un javascript de confirmation en mettant, un peu en dessous un script jQuery :

  1. <script type="text/javascript">
  2. (function($) {
  3. $('ul.liste_extras .supprimer').click(function(){
  4. return confirm("[(#VAL{iextra:supprimer_reelement}|_T|attribut_html)]");
  5. });
  6. })(jQuery);
  7. </script>

Télécharger

Et maintenant, créons le fichier d’action dans action/iextra.php et contenant :

  1. <?php
  2.  
  3. if (!defined("_ECRIRE_INC_VERSION")) return;
  4.  
  5. function action_iextra_dist() {
  6. $securiser_action = charger_fonction('securiser_action', 'inc');
  7. $arg = $securiser_action();
  8.  
  9. // droits
  10. include_spip('inc/autoriser');
  11. if (!autoriser('configurer', 'iextra')) {
  12. include_spip('inc/minipres');
  13. echo minipres();
  14. exit;
  15. }
  16.  
  17. list($arg, $id) = explode ('/', $arg);
  18.  
  19. // actions possibles
  20. if (!in_array($arg, array(
  21. 'supprimer_extra'))){
  22. include_spip('inc/minipres');
  23. echo minipres(_T('iextra:erreur_action',array("action"=>$arg)));
  24. exit;
  25. }
  26.  
  27. // cas de suppression
  28. if ($arg == 'supprimer_extra'){
  29. include_spip('inc/iextra');
  30. $extras = iextra_get_extras();
  31. if ($id = intval($id)) {
  32. // $id a 1 de plus
  33. unset($extras[--$id]);
  34. iextra_set_extras($extras);
  35. }
  36. }
  37.  
  38. }
  39. ?>

Télécharger

Les 2 premières lignes de la fonction vérifient que les paramètres soumis correspondent bien à l’auteur qui a cliqué le lien. Dans le cas contraire, un message d’erreur apparait et le script s’arrête (sur l’appel de $securiser_action();).

Cette fonction permet au passage de récupérer le deuxième argument de la balise, c’est à dire les arguments (séparés ici par des barres obliques (/)).

Une fois cette vérification faite, on vérifie que l’auteur a bien l’autorisation de configurer les champs extras (normalement c’est inutile vu que pour obtenir le lien il devait déjà avoir cette autorisation !).

Enfin, on effectue la suppression si c’est bien ce qui est demandé, pour l’instant, uniquement la suppression dans l’entrée de la table spip_meta. Le script redirigera ensuite automatiquement vers l’url donné par le troisième argument de la balise.

Déclarer les champs au plugin Champs Extras

Avant de gérer la création effective des champs dans les tables lorsqu’on les ajoute, nous allons commencer par déclarer les champs créés dans le pipeline correspondant. Cela va être utile pour la suite, car si les champs sont déclarés, SPIP peut gérer automatiquement leur création ensuite en appelant les fonctions adaptées.

Créons la fonction iextra_declarer_champs_extras_dist() dans le fichier base/iextra.php :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3.  
  4. function iextra_declarer_champs_extras_dist($champs=array()) {
  5. include_spip('inc/iextra');
  6. $extras = iextra_get_extras();
  7. foreach($extras as $e) {
  8. $champs[] = new ChampExtra($e);
  9. }
  10. return $champs;
  11. }
  12. ?>

Télécharger

Créer effectivement un champ lors de son ajout

Nous allons modifier la fonction traiter du formulaire pour qu’il crée effectivement le champ dans la table voulue lors de sa création.

On se crée une petite variable $new pour tester si l’entrée est nouvelle, puis on appelle la fonction maj_tables() :

  1. // l'enregistrer
  2. iextra_set_extras($extras);
  3.  
  4. // creer le champ s'il est nouveau :
  5. if ($new) {
  6. include_spip('base/create');
  7. $table = table_objet_sql($extra['table']);
  8. maj_tables($table);
  9. }

Télécharger

Supprimer effectivement le champ à la suppression

Même chose, on modifie l’action pour effectuer la suppression demandée :

  1. // cas de suppression
  2. if ($arg == 'supprimer_extra'){
  3. include_spip('inc/iextra');
  4. $extras = iextra_get_extras();
  5. if ($id = intval($id)) {
  6. // $id a 1 de plus
  7. $extra = $extras[--$id];
  8. unset($extras[$id]);
  9. iextra_set_extras($extras);
  10.  
  11. $table = table_objet_sql($extra['table']);
  12. sql_alter("TABLE $table DROP $extra[champ]");
  13. }
  14. }

Télécharger

Il faut récupérer les informations de l’extra avant de le supprimer pour pouvoir les utiliser dans la requête de suppression.

The End

Fin de ce chapitre qui a permi de découvrir un peu le principe des autorisations, des actions en base, des formulaires CVT, des fichiers exec...

Il y a bien sûr plein de choses à améliorer encore dans le plugin :

  • pouvoir ajouter des champs déjà créés dans les tables par des requêtes manuelles via phpMyAdmin,
  • gérer d’autres type que input text et textarea, comme les select et checkbox que permettaient les champs extras sérialisés de SPIP (passés en plugin eux-aussi)
  • gérer l’ordre d’affichage des champs extra
  • ...

En attendant, voici le plugin en zip, qui est aussi actualisé sur la zone. Ce zip est le résultat au terme de ce tutorial et fonctionne avec le plugin créé hier, et SPIP 2.0.2. Ce plugin d’interface et son parent vont certainement évoluer sur la zone au fil du temps. A suivre donc.

Coup de pouce

Pour me maintenir en éveil et en pleine forme physique et mentale, vous pouvez me faire le cadeau d'un jus de fruit pressé.

En plus de m'hydrater, de m'offrir une alimentation saine crudivore et frugivore, cela peut aussi me motiver à produire d'autres documentations ou peut-être à prendre des vacances ! :)

Vous pouvez également me « Flattrer » si vous utilisez le service en ligne très malin Flattr de microdonations qui permet d'allouer un budget mensuel à des créateurs de contenu. Votre budget est partagé chaque mois entre toutes les personnes que vous avez flattées ce mois là.