Plugin.xml et pipelines

Connaître les options du fichier plugin.xml

Proposition de documentation concernant la rédaction et le format du fichier plugin.xml des plugins pour spip (introduit dans SPIP 1.9.0), en complément de Réaliser un premier plugin, plugin_template/plugin.xml (accessoirement http://doc.spip.org/@Tuto-Se-servir... et rubrique 739).

Restrictions

Le fichier plugin.xml doit être écrit en XML valide si possible. En conséquence , il faut bien penser à utiliser les entités (e.g. à ou à pour à) à la place des caractères accentués.

Structure initiale

Comment lire :

  • < !— Ceci est un commentaire —>
  • titi|toto : soit titi, soit toto (ou exclusif)
<plugin>
    <!-- Nom du plugin -->
    <nom> NomPlugin </nom>

    <!-- Auteur du plugin -->
    <auteur> AuteurPlugin </auteur>
    <version>0.1</version>
    <version_base>0.1</version_base>
 
    <etat>dev|test|stable|experimental</etat>
    <icon></icon>
    <description> Un descriptif autant complet que possible, utilisation de la syntaxe spip autorisée.</description>
    <!-- URL de documentation -->
    <lien> une url interne ou bien sur le net </lien>

    <!-- précise le prefixe utilisé pour toutes les fonctions du plugin, en général le nom du plugin -->
    <prefix>prefixplugin</prefix>

    <!-- précise le fichier à charger à chaque recalcul, partie publique -->
    <fonctions>chemin du fichier </fonctions>
    <!-- précise le fichier à charger à chaque appel de la page, partie publique -->
    <options>chemin du fichier </options>

    <!-- Chemin du fichier d'installation et de mise à jour -->
    <install>chemin du fichier</install>

    <!--pipeline, surcharge de fonction à des moments précis de la génération d'une page SPIP -->
    <pipeline>
       <!-- pour connaître l'ensemble des pipelines voir :
     http://doc.spip.org/@Les-points-d-entree-pipelines
     http://trac.rezo.net/trac/spip/browser/spip/ecrire/inc_version.php -->
       <nom>point_entree</nom>
       <!-- nom de la fonction à appeler sans son prefixe -->
       <action>fonction</action>
       <!-- chemin du fichier contenant la fonction -->
       <inclure>fichier.php</inclure>
   </pipeline>

   <!-- précise les dépendances vis-à-vis de d'autres plugins (ou de spip), voir plus bas-->
   <necessite id="nomplugin" version="[versionminimale;versionmax]" />

   <!-- indique un zip externe nécessaire
     branche 1.9.3 à partir de spip svn 98xx
     id indique le répertoire où est chargé le zip,  /lib/nom -->
   <necessite id="lib:nom" src="http://url-complete-du/fichier.zip" />

   <!-- Indique si le plugin concerne tout le site ou seulement l'espace privé. -->
       <chemin dir="./repertoire_public" type="public">
       <chemin dir="./repertoire_prive" type="prive">
       <chemin dir="./repertoire_ouvert" >

   <bouton id='mon_bouton_1' parent=''>
       <icone>images/xml-valid-24.png</icone>
       <titre>Valider le site</titre>
       <url>w3c_go_home</url> <!-- nom de l'exec, facultatif, par defaut prend l'id -->
       <args>type=resume&id=1</args>
  </bouton>

  <onglet id='mon_onglet_1' parent='configuration'>
      <icone>images/img-24.png</icone>
      <titre>Onglet dans configuration</titre>
      <url>mon_onglet</url> <!-- nom de l'exec, facultatif, par defaut prend l'id -->
      <args></args>
  </onglet>

</plugin>

L’ordre des éléments xml n’a pas d’importance.

< description >

Cette partie est réservée à une description (assez courte) du plugin. Tu peux mettre tout ce que tu veux ici, à la seule condition de respecter les normes XML.

Pour assurer un maximum de compatibilité, il est parfois nécessaire de passer par les codes Unicode. Les accents de la langue française par exemple et autres caractères particuliers seront d’autant mieux restitués sur d’autres plateformes que la vôtre. Le "é" pourra donc être codé : &#233;.

Sous format XML, Il est très important de fermer toute balise ouverte. Pour utiliser une balise dans cette zone (par exemple pour documenter un modèle rajouté par le plugin), il est donc impossible d’utiliser la syntaxe <balise> (qui est une balise ouvrante). La meilleure solution est d’utiliser la table Unicode : <ModeleXX>. C’est un peu contraignant, mais on ne crée pas des plugins tous les jours !

Rappel : la façon de coder une balise unique (ouvrante et fermante à la fois) est la même qu’en HTML. Par exemple : <br />.

< install >

Lors de la déclaration de la balise <install>, tu indiques juste le
nom du fichier qui doit être appelé.

Il y a un consensus :

  • pour avoir ce fichier dans le répertoire base de ton plugin
  • d’utiliser le prefixe déclaré dans <prefixe>prefixeplugin</prefixe>
    Ce qui donne
    <install>base/prefixeplugin_upgrade.php</install>

Après dans ce fichier. On déclare une fonction :

function prefixeplugin_install($action){
    switch ($action){
        case 'test':
            //Contrôle du plugin à chaque chargement de la page d'administration
            // doit retourner true si le plugin est proprement installé et à jour, false sinon
        break;
        case 'install':
            //Appel de la fonction d'installation. Lors du clic sur l'icône depuis le panel.
            //quand le plugin est activé et test retourne false
        break;
        case 'uninstall':
            //Appel de la fonction de suppression
            //quand l'utilisateur clique sur "supprimer tout" (disponible si test retourne true)
        break;
    }
}

Donc ’test’ se lance à chaque fois qu’on accède à la page
d’administration des plugins. Cela peut donc servir lors des mises à
jours.

  • ’install’ sert aux opérations lors de l’activation du plugin.
  • ’uninstall’ sert aux opérations lors de la suppression du plugin.

Ces deux cas doivent returner du vide ... le test sera appelé ensuite.

Dans les exemples que j’ai pu voir test et install sont assez proches.

On peut aussi définir plusieurs fonctions dans le fichier <install>base/prefixeplugin_upgrade.php</install> comme ceci :

include_spip('base/create');
// installation des tables du plugin et mises à jour
function prefixeplugin_upgrade($nom_meta_base_version,$version_cible){
        $current_version = 0.0;
        if (   (!isset($GLOBALS['meta'][$nom_meta_base_version]) )
                        || (($current_version = $GLOBALS['meta'][$nom_meta_base_version])!=$version_cible)){
                if ($current_version==0.0){
                        include_spip('base/prefixeplugin');
                        creer_base();
                        ecrire_meta($nom_meta_base_version,$current_version=$version_cible);
                }
                if (version_compare($current_version,"0.1","<")){
                        // vos requêtes pour mettre à jour les tables du plugin
                        ecrire_meta($nom_meta_base_version,$current_version="0.1");
                }
                ecrire_metas();
        }
}
// désinstallation des tables du plugin
function prefixeplugin_vider_tables($nom_meta_base_version) {
        // vos requêtes pour effacer les tables du plugin
        effacer_meta($nom_meta_base_version);
        ecrire_metas();
}

< necessite >

Une nouveauté de SPIP 2.0

<necessite> sert pour 3 cas de figure (triés par ordre chronologique d’ajout dans le code de spip) :

  • une version d’un plugin <necessite id='CFG' version='[1.0;1.1)' />
  • une version de SPIP <necessite id='spip' version='[1.9.3.11965;]' />
    Format : spip_version_branche.spip_version_code
    Donc, par exemple :
    • 2.0
    • 2.0.11911
    • 1.9.2 (sans effet, mais documentant)
  • une bibliothèque à télécharger depuis le réseau <necessite id="lib:nom" src="http://url-complete-du/fichier.zip" />

Cas d’un plugin ou de spip

A changé en 11965.

Dans l’attribut version, écrivez les numéros de version, séparés par un point-virgule, et encadrés par des crochets ou des parenthèses. Mettre un crochet pour inclure, ou une parenthèse pour exclure, par exemple <necessite id='CFG' version='[1.0;1.1)' /> fait que le plugin requiert CFG version 1.0 minimum (comprise) et 1.1 maximum (exclue). Voir http://trac.rezo.net/trac/spip/chan.... Sachez que les versions sont comparées avec la fonction version_compare.

Attention, pour la version de SPIP, il ne s’agit pas de la version affichée et connue, mais de $spip_version_code, qui se trouve dans le fichier ecrire/inc_version.php. Par exemple : « $spip_version_code = 1.9207 ». Cela est succeptible de changer. (Si cette suggestion est accomplie, on pourrrait ajouter <necessite id='spip' version='[1.9.0;]' /> à tous les plugin.xml.)

Cas d’une bibliothèque

Une bibliothèque doit être un fichier .zip.

Le répertoire dans lequel sera dézippé la bibliothèque doit correspondre au nom fournit dans le nécessite.
Si nous avons <necessite id="lib:nom" src="http://url-complete-du/fichier.zip" />, le zip doit contenir comme répertoire racine nom, autrement charger_plugin reproposera au téléchargement la bibliothèque.

Pour savoir si une bibliothèque est correctement installée, vous pouvez utiliser la fonction find_in_path()

Exemple d’utilisation :

$cheminlib = find_in_path('lib/repertoirebibliotheque');
if (!$cheminlib)
   return $flux;

< utilise >

Une nouveauté de SPIP 2.0 (11964).

Identique à necessite, mais non impératif, uniquement indicatif. Par exemple pour indiquer un plugin pouvant être paramétré avec CFG

< version >

Il est possible de récuperer la version depuis plugin.xml. Cela evite une écriture redondante de la version entre les fichiers php et xml.
Dans le code suivant, prefixplugin est à remplacer par la valeur saisie dans la balise <prefix>

include_spip('inc/plugin');
//recupére les informations de plugin.xml
$infos = plugin_get_infos(_DIR_PLUGIN_PREFIXPLUGIN);
$version = $infos['version'];

Les versions étant comparées pour les dépendances (cf. élément necessite) avec la fonction version_compare, il est préférable d’utiliser des versions au format indiqué dans la doc de version_compare (par exemple successivement 0.1, 0.2, 0.2.1, 0.2.2, 0.3, 1.0, 1.0.1).

< version_base >

version_base permet de gérer spécifiquement les gestions de mises à jour de la base de donnée. Par exemple dans le fichier d’install défini dans base/prefixeplugin_upgrade.php

   function prefixeplugin_upgrade($nom_meta_base_version,$version_cible){
           $champs = /*déclaration des champs propre au plugin */
   }
   function prefixeplugin_vider_tables($nom_meta_base_version) {
           $champs = /*déclaration des champs propre au plugin */
   }

< prefix >

Lors de l’activation du plugin SPIP met en place des constantes globales. Parmi celle ci se trouve
_DIR_PLUGIN_PREFIXPLUGIN qui indique le chemin du plugin depuis la racine.

Remarque 1 : Des underscores peuvent être utiliser dans le prefixe. Toutefois les fichiers de lang devront être sans.
Si le prefixe est pre_fix, les fichiers de lang seront de la forme prefix_lg.php (avec lg l’abreviation de la langue).

Remarque 2 : _DIR_PLUGINS mène au répertoire des pluginS et _DIR_PLUGIN_PREFIXPLUGIN mène au répertoire du plugin préfixé PREFIXPLUGIN ou PrefixPlugin.

Remarque 3 : En spip 1.9.3, en raison du dispositif de chargement automatique des plugins, il est impératif d’utiliser _DIR_PLUGIN_PREFIXPLUGIN pour pointer une ressource.

< bouton > et < onglet >

Une nouveauté de SPIP 1.9.3 (http://trac.rezo.net/trac/spip/tick..., http://trac.rezo.net/trac/spip/chan..., http://trac.rezo.net/trac/spip/chan...).

« pour ajouter des boutons dans les menus de l’espace prive sans coder :

  • pour ajouter un bouton dans le bandeau principal
    <bouton id='mon_bouton_1' parent=' '>
        <icone>images/xml-valid-24.png</icone>
        <titre>Valider le site</titre>
        <url>w3c_go_home</url> <!-- nom de l'exec, facultatif, par defaut prend l'id -->
        <args>type=resume&id=1</args>
    </bouton>
  • pour ajouter un bouton dans le bandeau secondaire
    <bouton id='mon_sous_bouton' parent='mon_bouton_1'>
        <icone>images/xml-valid-24.png</icone>
        <titre>Valider le site</titre>
        <url>w3c_go_home</url> <!-- nom de l'exec, facultatif, par defaut prend l'id -->
        <args>type=complet&id=1</args>
    </bouton>

     » (adapté de http://trac.rezo.net/trac/spip/chan...)

Exemple dans http://trac.rezo.net/trac/spip-zone...

« le bandeau secondaire » correspond aux boutons liés à un bouton du bandeau principal, pas à la deuxième ligne (les boutons « Tout le site », « Navigation rapide », « Rechercher »... « Se déconnecter »). Les codes/identifants de boutons de premier niveau existants et acceptables pour l’attribut parent (d’un bouton de deuxième niveau) sont ceux du du bandeau principal <(en haut de ?exec=) :

  • accueil
  • naviguer
  • forum
  • auteurs
  • statistiques_visites
  • configuration
  • aide_index
  • visiter
    (on verra plus tard si il y a l’utilité d’autres emplacements dans l’interface privé)
    (plus ceux ajoutéds auparavent par un plugin)

Le permier exemple ci-dessus fait créer un bouton dans le bandeau principal, avec sur le bouton « Valider le site » et l’image images/xml-valid-24.png, et renvoyant vers ?exec=w3c_go_home&type=complet&id=1.

Vous pouvez utiliser une structure similaire avec <onglet> pour ajouter un onglet à une page. Il faut que cette page puisse afficher les onglets, ceci est défini par l’utilisation dans la page de la fonction "barre_onglet" comme ici dans /exec/configuration.php : echo barre_onglets("configuration", "contenu");

< chemin >

La balise chemin et son attribut dir permettent de déclarer dans le @find_path les repertoires du plugin accessibles automatiquement.
Si aucun chemin n’est déclaré, uniquement les repertoires ayant la meme arborescence que SPIP sont accessibles : ./, exec/, ....
La déclaration par défaut est donc <chemin dir="" />.

Il est possible de préciser dans quel cadre le repertoire est accessible :

  • uniquement depuis l’espace public type="public"
  • uniquement depuis l’espace privé type="prive"
  • pour les 2 à la fois, on ne précise pas le type.

Si vous déclarez un chemin spécifique, la régle par défaut est annulée, il faut donc la déclarer explicitement par une deuxième balise chemin si vous en avez aussi besoin. Par exemple pour utiliser des formulaires CVT dans l’espace privé en plus du comportement habituel il faut écrire :

  • <chemin dir="" />, necessaire pour rendre la racine du plugin accessible
  • <chemin dir="./prive" type="prive" />

Le nombre de chemin autorisé dans plugin.xml est illimité.

< categorie >

La balise categorie permet de classer le plugin dans une famille. Cette balise n’est pas encore réellement prise en compte (au 16/01/2010) dans SPIP mais les futurs outils de recherche de plugin pourront l’exploiter (un prototype nommé « STEP » le permet déjà).

La liste des catégories est fixe et se compose de 13 entrées différentes. L’identifiant est à renseigner dans plugin.xml :
<categorie>edition</categorie>. Voici la liste complète :

identifiant titre explications complémentaires à l’usage des concepteurs de plugins
auteur Authentification
Auteur
Autorisation
communication Communication
Interactivité
Messagerie
date Agendas
Calendrier
Date
divers Objets nouveaux
Services externes
edition Édition
Impression
Rédaction
maintenance Configuration
Maintenance
multimedia Images
Galerie
Multimedia
navigation Navigation
Organisation
Recherche
outil Outil de développement indiquer cette catégorie pour les plugins qui n’opèrent aucune modification ni dans l’interface privée ni dans l’interface publique, mais qui sont utilisés par d’autres plugins.
performance Optimisation
Performance
Sécurité
squelette Squelette
statistique Référencement
Statistique
theme Thème

Note :
Il est envisagé, en plus de la balise <categorie> d’introduire une balise <tag>, avec de la même façon une liste de tag, et la possibilité d’en définir plusieurs pour un plugin : <tag>authentification,auteur,securite</tag>.

Précision sur les répertoires d’un plugin

On sort ici du cadre strictement du fichier plugin.xml (ceci sera à mettre dans un article propre)
La structure de base d’un plugin sera :

./
./prefix_options.php
./prefix_fonctions.php
./action/
./balises/
./base/
./exec/
./fonds/
./formulaires/
./lang/
./public/
action/ pour effectuer des traitements
base/ pour mettre le fichier d’installation
exec/ pour gérer des affichages spécifique coté privé
fonds/ les squelettes (dont ceux pour cfg)
lang/ les traductions
public/ les déclaration de balises personnalisées

Précision sur action/ et exec/

action

Les script contenus dans action/ servent à faire des traitements comme :

  • inserer des données dans une base de données
  • écrire un fichier
  • créer un repertoire
  • ...

En gros tout ce qui ne nécéssite pas un affichage d’information.

Les scripts sont de la forme action_plugin_fonction_dist() avec :

  • action : préfixe de fonction imposé
  • plugin : le nom du plugin utilisant l’action
  • fonction : un nom de fonction
  • dist : ceci n’est pas obligatoire mais offre la possibilité d’une surcharge

Le fichier associé sera action/plugin_fonction.php

ATTENTION (à vérifier par un pro de SPIP) : certains plugins créés avec la version 1.9.2 de SPIP utilisent le paramètre "action" dans l’url style : / ?exec=truc&action=machin. Depuis la version 2.0 de SPIP, "action" est un mot réservé. Il faut remplacer "action" par "param" (par exemple) dans le plugin pour qu’il fonctionne avec la version 2.0 de SPIP.

Ou alors machin doit renvoyer à une action dans le fichier "machin.php"

exec

Dans ce repertoire se trouvent les script qui sont dédiés à l’affichage. Ces scripts en tant que tel ne font aucun traitement mais pourront faire appel a des actions.

Un exec de base contiendra la plupart du temps un ou des formulaires qui feront appel à des action/
On pourra aussi penser à utiliser la fonction recuperer_fond() pour exploiter les squelettes

Les scripts sont de la forme exec_plugin_fonction_dist() avec :

  • exec : préfixe de fonction imposé
  • plugin : le nom du plugin utilisant l’action
  • fonction : un nom de fonction
  • dist : ceci n’est pas obligatoire mais offre la possibilité d’une surcharge

Le fichier associé sera exec/plugin_fonction.php

Charger un squelette

Si vous placez les fichiers d’un squelette à la racine de votre plugin (par exemple dans plugins/mon_plugin/) et activez ce plugin (qui contient les attributs normaux d’un plugin, comme le fichier plugin.xml, etc) cela vous permet d’avoir un squelette embarqué dans un plugin.

N’oubliez pas que spip va choisir le fichier de squelette à charger en suivant l’ordre de priorité décroissante suivant :
squelettes/ > plugins/ > dist/ > ecrire/.

Par exemple, pour un fichier a.html, c’est /squelettes/a.html qui sera prioritaire sur plugins/mon_plugin/a.html. De la même façon, s’il y a les fichiers /plugins/mon_plugin/a.html, plugins/mon_plugin/b.html et squelettes/a.html, spip affichera squelettes/a.html et plugins/mon_plugin/b.html (le fichier squelettes/b.html n’existant pas).

Plus généralement, cela vaut aussi pour tous les sous-dossiers (/plugins/mon_plugin/modeles/a.html chargé à la place de /dist/modeles/a.html), ainsi que pour n’importe quel type de fichier, pas juste les .html.

, par cam.lafit