Overview

Artifact ID: 2f51325171ba83131fb427fd12a2c571e1607ded3cc9574d23683197e7b2449b
Page Name:Plugins
Date: 2021-09-22 01:05:41
Original User: bohwaz
Mimetype:text/x-markdown
Parent: 4574be4d1dae50923056ea69a9f57b86dc7b69ac9625796f1744bd6781ef45e7 (diff)
Next 8c0598bc7a3420d96435aef13a28ee1739d42e5339e93b0ea800bbfe3eb3bd84
Content

Développer un plugin

Vous cherchez comment installer un plugin ? Voir la documentation webmaster :)

Garradin permet d'étendre ses fonctionnalités avec des plugins (appelés extensions dans la partie privée/admin de Garradin).

L'interface de Garradin est séparée en deux sections : la section publique correspond au site web de l'association, accessible à tout visiteur, et la section privée, aussi appelée administration de l'association. Les plugins peuvent étendre l'une ou l'autre partie, ou même les deux.

Évolutions

Les versions majeures peuvent apporter des changements qui cassent les plugins, voir Plugins/Evolutions

Installation

Les plugins peuvent être installés de deux manières :

  • par le catalogue des extensions (extensions officielles) [non fonctionnel pour le moment]
  • simplement en copiant le fichier de l'extension dans le répertoire plugins de Garradin (extensions non-officielles)

Développement

Pour faciliter le développement de plugins il est aussi possible de placer le code du plugin dans un sous-répertoire de plugins, ainsi il n'est pas nécessaire de re-packager le plugin à chaque modification.

Bonnes pratiques

Les plugins officiels sont relus et vérifiés avant publication et garantissent qu'ils ne devraient pas atteindre au bon fonctionnement de Garradin ou des autres plugins.

Pour pouvoir être intégré au catalogue des plugins officiels il faut qu'il respecte un ensemble de règles édicté plus bas : règles du catalogue.

Les plugins non-officiels ne sont soumis à aucune règle mais il est quand même recommandé de suivre celles qui sont indiquées ici.

Règles du catalogue

Pour intégrer le catalogue des plugins officiels disponibles directement depuis Garradin le plugin doit :

  • être sous licence libre (compatible Debian et FOSS : GPL, AGPL, BSD, etc.) afin de pouvoir être maintenu dans le temps par la communauté ;
  • avoir un code lisible et clair (peu importe la convention de codage) ;
  • ne pas porter atteinte à l'intégrité et la stabilité de Garradin, cela implique :
    • NE PAS patcher, éditer, supprimer ou ajouter de fichiers au code de Garradin lui-même : le plugin doit être contenu dans son archive. Il est par contre possible de faire dépendre un plugin de code ou composants contenus dans un autre plugin.
    • NE PAS stocker de données en dehors de la base de données de Garradin.
    • NE PAS ajouter, modifier ou supprimer directement des données dans la base de données de Garradin (hormis dans sa propre table).
    • NE PAS enregistrer de documents sans utiliser les méthodes fournies par Garradin.
  • respecter la vie privée des utilisateurs et notamment :
    • NE PAS contacter un serveur distant (sauf besoin spécifique) ni transmettre des informations ou statistiques à un serveur tiers.

Détails techniques

Format des plugins

Les plugins sont des archives .tar.gz : tous les fichiers du plugin (code PHP, CSS, templates, images, etc.) sont stockés dans l'archive et y restent (l'archive n'est jamais décompressée).

Depuis la version 0.9.0 il est aussi possible de simplement avoir le plugin dans un sous-répertoire du répertoire des plugins. Ceci permet de faciliter le développement ou la maintenance du plugin. Pour la distribution d'un plugin, le format .tar.gz est toujours préféré.

Il est possible de créer soi-même l'archive .tar.gz de cette manière, depuis le répertoire racine du plugin :

$ tar czvf src/plugins/test.tar.gz ./

Un script PHP (make_plugin.php) est fourni dans le répertoire tools de la version de développement de Garradin afin de vérifier l'arborescence et de créer l'archive en ligne de commande. Son utilisation est très simple :

$ php ~/garradin/tools/make_plugin.php ~/dev/plugins/test ~/garradin/src/plugins/test.tar.gz

Informations sur le plugin (garradin_plugin.ini)

Chaque plugin doit au moins fournir à la racine de son archive un fichier nommé garradin_plugin.ini. Ce fichier fournit le nom, la description et quelques détails sur le plugin :

nom="Ma première extension Garradin"
description="Affiche un message trop cool"
auteur="Anne Onyme"
url="http://garradin.eu/"
version="1.0"
menu=1
config=1
min_version="0.7.0"

Explication des directives :

  • nom indique le nom du plugin, qui sera utilisé dans les listes et menus.
  • description fournit une description plus longue, peut faire plusieurs lignes.
  • auteur donne le nom de l'auteur du plugin
  • url indique l'adresse du site web de l'auteur.
  • version est utilisée pour savoir quelle est la version du plugin fournie dans l'archive. Voir plus loin : Mise à jour.
  • menu indique à Garradin s'il doit afficher une entrée dans le menu des extensions de la partie privée. Cette entrée sera visible par tous les membres qui peuvent se connecter, même si l'extension restreint ensuite son accès. L'entrée du menu appellera le fichier www/admin/index.php qui devra donc exister, à défaut l'installation de l'extension échouera.
  • config indique si l'extension possède une configuration modifiable par l'utilisateur. Si positionné à 1 (ou true) alors la page de gestion des extensions proposera un lien pour configurer l'extension (appelant le fichier www/admin/config.php), et le fichier config.json sera importé comme configuration par défaut. Ces deux fichiers sont donc obligatoires si config est activé.
  • min_version est facultatif et peut comporter le numéro de version de Garradin minimal nécessaire à l'installation du plugin. Par exemple si on renseigne "0.8.0" et que la version installé de Garradin est la 0.7.1, le plugin refusera de s'installer.

Attention, si le fichier garradin_plugin.ini n'existe pas dans le plugin il ne pourra pas être installée, n'étant pas considérée comme un plugin de Garradin.

Configuration du plugin

Si config est positionné à 1 ou true (ou même On) dans garradin_plugin.ini la configuration au format JSON stockée dans config.json sera importée comme configuration par défaut du plugin.

Attention les objets javascript sont transformés en tableaux à l'import, il n'est donc pas possible de stocker un objet dynamique dans la configuration.

Le plugin peut ensuite lire sa configuration depuis son code, avec $plugin->getConfig(string $key), et changer ses infos en faisant $plugin->setConfig(string $key, string $value).

Scripts magiques

Chaque archive peut comporter certains scripts magiques qui seront appelés automatiquement par Garradin lors de certains événements.

  • install.php est appelé quand le plugin a été téléchargé et qu'il est déjà noté comme installé (post-installation), utile notamment pour créer une table dans la base de données
  • upgrade.php : quand la version de l'archive (notée dans garradin_plugin.ini) ne correspond pas à la version enregistrée en base de donnée
  • uninstall.php : juste avant que le plugin ne soit supprimé, utile par exemple pour supprimer une table créée dans la base de données
  • signals.php : inclus quand un signal enregistré est appelé

Ces scripts ne peuvent pas être appelés par une requête HTTP via l'interface privée ou publique.

Objet GarradinPlugin

  • __construct(string $id)
  • setConfig(string $key, string $value) : enregistre la configuration du plugin, si $value est null alors toute clé est effacée de la configuration
  • getConfig(string $key) : récupère la valeur de la clé $key pour la configuration du plugin
  • getInfos() : renvoie les informations enregistrées sur le plugin
  • upgrade() : mise à jour du plugin
  • needUpgrade() : le plugin doit-il être mis à jour ?
  • uninstall() : désinstaller le plugin
  • id() : renvoie l'identifiant du plugin
  • path() : renvoie le chemin vers l'archive du plugin
  • registerSignal(string $signal, string $callback) : enregistre un callback associé à un signal

Base de données

Tous les plugins ont un accès illimité à la base de données principale de Garradin. Cependant il est interdit d'ajouter, modifier ou supprimer des données directement dans les tables de cette BDD afin de ne pas compromettre l'intégrité des données. Pour modifier ces données il faut utiliser les méthodes de Garradin.

Chaque plugin peut créer une ou plusieurs tables dans cette BDD, elles devront par contre être supprimées à la désinstallation. Dans ce cas un plugin peut modifier directement ses tables.

Fichiers

Les plugins ne devraient pas créer, modifier ou supprimer de fichiers dans l'arborescence de Garradin.

Les seuls fichiers qu'un plugin devrait modifier sont :

  • du cache : utiliser de préférence l'objet Static_Cache de Garradin, ou faire attention aux collisions de noms ;
  • des pages web du site public dans www/

Pour enregistrer et récupérer des documents il faut utiliser les méthodes de stockage de fichiers fournies par Garradin.

Signaux

Les plugins peuvent se « brancher » sur des signaux émis par Garradin lors de certaines actions.

Les signaux accessibles sont :

  • skriv.init (depuis 0.8.0) permet d'enregistrer des extensions Skriv depuis les plugins
  • usertemplate.init (depuis 1.1.0) est exécuté lors de l'initialisation d'un User Template (voir plus bas), pour pouvoir étendre le langage Brindille utilisé dans les modèles de documents et les squelettes du site web public
  • entity.NAME.save.before (depuis 1.1.0) est exécuté AVANT l'enregistrement d'une entité (pour savoir si une entité existe et donc sera modifiée ou créée, utiliser $entity->exists())
  • entity.NAME.save.after (depuis 1.1.0) est exécuté APRÈS l'enregistrement d'une entité
  • entity.NAME.delete.before (depuis 1.1.0) est exécuté AVANT la suppression d'une entité
  • entity.NAME.delete.after (depuis 1.1.0) est exécuté APRÈS la suppression d'une entité
  • files.store (depuis 1.1.0) exécuté après l'enregistrement d'un fichier (création ou modification), avec le paramètre file contenant l'objet File correspondant
  • files.mkdir (depuis 1.1.0) exécuté à la création d'un répertoire, paramètre idem
  • files.delete (depuis 1.1.0) exécuté après la suppression d'un fichier, paramètre idem
  • files.move (depuis 1.1.0) exécuté après le déplacement d'un fichier, paramètre idem, avec un paramètre new_path supplémentaire pointant sur le chemin de destination
  • http.request (depuis 1.1.11) est exécuté lors de la réception d'une requête HTTP traitée par le routeur HTTP de Garradin, AVANT de déterminer quoi faire de cette requête. Un paramètre uri est fourni. Les requêtes sur des pages PHP classiques (par exemple administration) ne sont pas traitées par ce signal. Si un plugin renvoie TRUE à ce signal, Garradin arrêtera son exécution à ce point, pensant que le plugin à répondu à la requête HTTP.
  • http.request.file.before (depuis 1.1.11) est exécuté AVANT le traitement d'une requête HTTP qui correspond à un fichier. Paramètres fournis : uri et file (contenant une entité File). Renvoyer TRUE interrompt l'exécution.
  • http.request.file.after (depuis 1.1.11) : identique, mais exécuté APRÈS le traitement de la requête, renvoyer TRUE ne change rien.
  • http.request.skeleton.before (depuis 1.1.11) est exécuté AVANT le traitement d'une requête HTTP qui correspond à un squelette. Paramètres fournis : uri, skeleton (contient le nom du squelette correspondant à la requête) et page qui contient une entité Page ou NULL si l'URI fournie ne correspond à aucune page connue. Renvoyer TRUE interrompt l'exécution.
  • http.request.skeleton.after (depuis 1.1.11) : identique, mais exécuté APRÈS le traitement de la requête, renvoyer TRUE ne change rien.

Signaux dépréciés

Ces signaux seront renommés en anglais dans une prochaine version :

  • membre.nouveau, appelé juste après l'insertion du membre dans la base de données
  • membre.suppression, appelé juste avant la suppression d'un ou plusieurs membres de la base de données
  • rappels.auto (depuis 0.8.0) appelé après l'envoi automatique des emails de rappel
  • email.envoi (depuis 0.9.0) appelé avant l'envoi effectif d'un email, permet à un plugin d'envoyer les emails à la place Garradin (par exemple pour utiliser une API d'envoi etc.), si la méthode du plugin renvoie TRUE alors Garradin considérera que le mail a été envoyé et n'essaiera pas de l'envoyer lui-même.
  • motdepasse.compromis (depuis 0.9.6) appelé pour vérifier si un mot de passe est compromis ou non. Le plugin doit renvoyer TRUE pour arrêter la chaîne d'exécution, et positionner le booléen is_compromised à vrai ou faux dans le tableau des paramètres de retour.

Enregistrement des signaux

Un plugin peut se connecter à un signal lors de son installation ou de sa mise à jour :

$plugin->registerSignal('entity.Accounting\Transaction.save.after', 'Garradin\Plugin\MonPlugin\Tracking::trackTransactionChange');

La méthode indiquée en second paramètre sera appelée à chaque fois que le signal est déclenché, avec deux arguments, le premier est un tableau de paramètres, et le second un tableau de valeurs à retourner (utilisé pour les boucles, pas pour les autres).

Les méthodes de callback des signaux doivent obligatoirement commencer par le namespace Garradin\Plugin\NomDuPlugin (où NomDuPlugin correspond à l'identifiant du plugin), Garradin utilisera l'auto-chargement des classes (selon PSR-0) pour appeler la méthode. Par exemple en enregistrant un callback sur Garradin\Plugin\Caisse\Signaux::addMember, Garradin chargera le fichier lib/Signaux.php du plugin caisse (notez les minuscules pour le nom du répertoire ou archive tar.gz du plugin) et appellera la méthode statique addMember de la classe Signaux située dans le namespace Garradin\Plugin\Caisse.

Si une méthode d'un plugin appelée lors de l'émission d'un signal renvoie TRUE alors les autres méthodes enregistrées pour le même signal ne seront pas appelées.

Dans les templates

Garradin fournit la lib Smartyer pour les pages privées (répertoire /admin/), où elle est déjà chargée par défaut. C'est une version allégée du moteur de template Smarty 2. Voir la documentation de Smartyer

Afficher un template contenu dans le plugin :

$tpl->display(PLUGIN_ROOT . '/templates/index.tpl');

Inclure un template de Garradin depuis un template :

{include file="admin/_head.tpl"}

Inclure un template du plugin depuis un autre template du plugin :

{include file="%s/templates/_nav.tpl"|args:$plugin_root}

Faire un lien vers une autre page du plugin :

<a href="{plugin_url file="liste.php"}">Liste des trucs</a>

Inclure une feuille de style CSS supplémentaire sur les pages du plugin (chemin relatif à la racine du plugin) :

$tpl->assign('plugin_css', 'styles/bleu.css');

User Templates

Le site public de l'association n'utilise pas Smartyer mais des squelettes, qui utilisent le langage Brindille.

Ces squelettes sont basés sur les User Templates, qui peuvent servir pour faire des modèles de documents internes, en plus des squelettes du site web.

  • Le langage de base est Brindille
  • Garradin étend ce langage avec les User Templates (qui peuvent être utilisés comme modèle de document, modèle d'email, etc.) (classe UserTemplate)
  • Les UserTemplates sont étendus à leur tour pour faire les Squelettes (classe Skeleton)

Il est possible d'étendre les UserTemplates avec de nouvelles fonctions, sections (boucles), ou modifieurs, simplement en les enregistrant à l'objet UserTemplate lors de son initialisation, avec le signal usertemplate.init.

Un exemple de plugin qui rajoute une section appelée velos aux templates, à l'installation enregistre le signal :

$plugin->registerSignal('usertemplate.init', 'Garradin\Plugin\Stock_Velos\Velos::register');

Et dans le fichier lib/Velos.php du plugin on ajoute cette méthode :

    static public function register(array $params)
    {
        $ut =& $params['template'];

        $ut->registerSection('velos', [self::class, 'section']);
    }

Et le code de la section elle-même, qui doit obligatoirement renvoyer un générateur (yield) :

    static public function section(array $params)
    {
        if (isset($params['count'])) {
            $sql = 'SELECT COUNT(*) AS count FROM plugin_stock_velos WHERE prix > 0 AND date_sortie IS NULL;';
        }
        else {
            $sql = 'SELECT prix, modele, roues, type, genre, etiquette FROM plugin_stock_velos
                WHERE date_sortie IS NULL AND prix > 0 ORDER BY date_entree ASC;';
        }

        $db = DB::getInstance();
        foreach ($db->iterate($sql) as $row) {
            yield (array) $row;
        }
    }