Signaux dans les plugins

Les plugins peuvent se « brancher » (connecter) sur des signaux (événements) émis par Paheko lors de certaines actions.

Utilisation

Se connecter à un signal

Il faut dans le fichier install.php du plugin enregistrer son callback associé au signal souhaité, avec la méthode registerSignal de l'entité Plugin (fournie par défaut dans la variable $plugin) :

$plugin->registerSignal('home.banner', 'Paheko\Plugin\Test\MyTest::banner');

Le premier paramètre est le nom du signal (voir ci-dessous pour la liste), le second est le nom du callback, qui doit être une fonction statique publique d'une classe.

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

Réagir à un signal

Quand un signal est lancé, la méthode spécifie comme callback est exécutée. Le premier paramètre passé est un objet Signal, et le second est l'objet Plugin correspondant au plugin qui a enregistré le signal.

Paramètres d'entrée, paramètres de retour

Un signal peut être appelé avec des paramètres en entrée, dans ce cas on appelle la méthode Signal->getIn(?string $name). On ne peut pas modifier ces paramètres.

Un signal peut aussi renvoyer des valeurs. Dans ce cas on peut appeler la méthode Signal->setOut(string $name, mixed $value).

Les paramètres possibles diffèrent d'un signal à l'autre.

namespace Paheko\Plugin\Test;
use Paheko\Entities\Signal;

class MyTest
{
    static public function banner(Signal $signal, Plugin $plugin)
    {
        $signal->setOut('my_banner', '<h1>Test !!!</h1>');
    }
}

Arrêter une exécution

Un plugin peut décider de stopper l'exécution en appelant la méthode Signal->stop(). Dans ce cas, les callbacks suivants dans la liste ne seront pas appelés, et la méthode déclenchant le signal arrête son exécution également.

Si le signal n'est pas stoppable, une exception LogicException sera levée.

Exemple, si vous souhaitez gérer la file d'envoi de mails dans un plugin :

$plugin->registerSignal('email.queue.insert', 'Paheko\Plugin\CustomEmailQueue\Queue::insert');
…
    static public function insert(Signal $signal, Plugin $plugin)
    {
        …
        $db->insert('my_queue', ['recipient' => $signal->getIn('recipient')]);
    }

Liste des signaux

Squelettes et templates

  • render.extensions.init (depuis 1.2.7) permet d'enregistrer des extensions Skriv/MarkDown depuis les plugins
  • usertemplate.init (depuis 1.1.0) est exécuté lors de l'initialisation d'un User Template (voir la documentation développeur des extensions), pour pouvoir étendre le langage Brindille utilisé dans les modèles de documents et les squelettes du site web public

Entités

Il existe des signaux spécifiques contenant le nom de l'entité :

  • entity.NAME.save.before (depuis 1.1.0) est exécuté AVANT l'enregistrement d'une entité (qu'elle ait été modifiée ou non)
  • entity.NAME.save.after (depuis 1.1.0) est exécuté APRÈS l'enregistrement d'une entité
  • entity.NAME.create.before (depuis 1.3.0) est exécuté AVANT la création d'une nouvelle entité
  • entity.NAME.create.after (depuis 1.3.0) est exécuté APRÈS la création d'une nouvelle entité
  • entity.NAME.modify.before (depuis 1.3.0) est exécuté AVANT la modification d'une nouvelle entité (seulement si une propriété de l'entité à été modifiée)
  • entity.NAME.modify.after (depuis 1.3.0) est exécuté APRÈS la modification d'une nouvelle 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é

Ce nom contient le namespace partiel de l'entité : la classe Paheko\Entities\Users\User donnera lieu à l'appel des signaux entity.Users\User....

Et des signaux génériques (depuis 1.1.12) qui concernent toutes les entités (quand on veut cibler toutes les créations ou suppressions d'entités par exemple). Ces signaux sont exécutés après le signal spécifique (contenant le nom de l'entité) :

  • entity.save.before
  • entity.save.after
  • entity.create.before
  • entity.create.after
  • entity.modify.before
  • entity.modify.after
  • entity.delete.before
  • entity.delete.after

Les signaux before peuvent interrompre l'action, mais pas after.

Membres

  • password.check (depuis 1.3.0) : exécuté AVANT la vérification de compromission d'un mot de passe. 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.
  • user.login.after (depuis 1.3.0) : exécuté après une tentative de connexion. Voir la clé success pour savoir si la connexion a fonctionné ou non. success peut être false (mauvais login ou mot de passe), true (connexion OK) ou la chaîne OTP si la connexion a fonctionné mais que le membre nécessite du 2FA en OTP
  • user.login.otp.after (depuis 1.3.0) : exécuté après une tentative de 2FA avec OTP. Voir la clé success (booléen) pour savoir si le code OTP était bon ou non.
  • user.change.login.after (depuis 1.3.0) : exécuté après le changement d'identifiant de connexion d'un membre
  • user.change.password.after (depuis 1.3.0) : exécuté après le changement de mot de passe de connexion d'un membre

Fichiers

  • file.store (depuis 1.3.0) exécuté après l'enregistrement d'un fichier (création ou modification), avec le paramètre file contenant l'objet File correspondant
  • file.mkdir (depuis 1.3.0) exécuté à la création d'un répertoire, paramètre idem
  • file.delete (depuis 1.3.0) exécuté après la suppression d'un fichier, paramètre idem
  • file.rename (depuis 1.3.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
  • file.create (depuis 1.3.0) exécuté après la création d'un nouveau fichier non-vide
  • file.overwrite (depuis 1.3.0) exécuté après l'écrasement du contenu d'un fichier existant (modification du contenu)
  • file.trash (depuis 1.3.0) exécuté après le déplacement d'un fichier vers la corbeille
  • file.restore (depuis 1.3.0) exécuté après le déplacement d'un fichier de la corbeille vers son chemin d'origine

Requêtes 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, session (contenant la session de l'utilisateur courant, s'il est connecté, sinon NULL), 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.
  • web.request.before (depuis 1.3.0) 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.
  • web.request.after (depuis 1.3.0) : identique, mais exécuté APRÈS le traitement de la requête, renvoyer TRUE ne change rien.

Création de PDF

Ces signaux sont réservés à une extension qui fournirait un moyen de créer des PDF à partir de fichiers HTML. Le but est de fournir une alternative aux associations auto-hébergées dont le serveur ne dispose pas de programme de création de PDF. Une extension Dompdf est disponible à cette fin.

  • pdf.stream (depuis 1.1.12) est utilisé quand un PDF doit être envoyé directement au navigateur. Dans ce cas un seul paramètre string est fourni, il contient le code HTML à transformer en PDF.
  • pdf.create (depuis 1.1.12) est utilisé quand un PDF doit être stocké dans un fichier. Deux paramètres sont fournis : source est le chemin complet du fichier HTML source, et target est le chemin complet du PDF qui doit être créé.

L'extension doit renvoyer TRUE dans ces signaux pour interrompre la recherche automatique de programme de création de PDF : si l'extension ne renvoie pas TRUE, Paheko continuera à chercher un programme et à éventuellement l'exécuter.

Attention, ces signaux ne sont pas déclenchés si la constante de configuration PDF_COMMAND n'a pas null comme valeur, car on considère alors que le PDF est créé par la commande donnée, et appeler l'extension n'a donc aucun sens.

E-mails et rappels

  • reminder.send.after (depuis 1.1.25) appelé après l'envoi automatique d'un e-mail de rappel (un appel par message)
  • email.send.before (depuis 1.1.25) appelé avant l'envoi effectif d'un email, permet à un plugin d'envoyer les emails à la place Paheko (par exemple pour utiliser une API d'envoi etc.), si la méthode du plugin renvoie TRUE alors Paheko considérera que le mail a été envoyé et n'essaiera pas de l'envoyer lui-même.
  • email.send.after (depuis 1.1.25)
  • email.queue.before (depuis 1.1.25) appelé avant le début de mise en queue d'un message à un ou plusieurs destinataires. C'est ici qu'il faut intervenir si on veut empêcher l'utilisateur d'envoyer un message, l'appel à email.send.* pouvant se faire de manière asynchrone.
  • email.queue.after (depuis 1.1.25)
  • email.queue.insert (depuis 1.1.25) appelé pour l'insertion de chaque message la file d'attente, avec le message personnalisé pour le destinataire
  • email.bounce (depuis 1.1.28) est appelé quand un message de bounce est reçu

Autres

  • cron (depuis 1.1.12) est exécuté de manière périodique en même temps que les autres tâches périodiques. Malheureusement il n'est pas possible de prévoir la régularité d'une telle exécution, car en auto-hébergement par défaut cette fonction est lancée à la connexion, à moins d'avoir activé la constante USE_CRON dans la configuration et d'exécuter le script www/cron.php directement depuis un cron système.
  • home.banner (depuis 1.1.25) permet d'afficher une bannière en haut de la page d'accueil (affichée après la connexion). La fonction doit simplement renvoyer une chaîne HTML qui sera affichée en haut de la page (dans le second paramètre reçu par référence).
  • menu.item (depuis 1.1.25) permet d'insérer des items dans le menu principal, en dessous de l'item "page d'accueil".
  • home.button (depuis 1.3.0) permet d'insérer des boutons sur la page d'accueil
  • web.page.version.new (depuis 1.3.0) appelé après la modification du contenu d'une page web (nouvelle version du texte)