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 pluginsusertemplate.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 renvoyerTRUE
pour arrêter la chaîne d'exécution, et positionner le booléenis_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 êtrefalse
(mauvais login ou mot de passe),true
(connexion OK) ou la chaîneOTP
si la connexion a fonctionné mais que le membre nécessite du 2FA en OTPuser.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 membreuser.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ètrefile
contenant l'objetFile
correspondantfile.mkdir
(depuis 1.3.0) exécuté à la création d'un répertoire, paramètre idemfile.delete
(depuis 1.3.0) exécuté après la suppression d'un fichier, paramètre idemfile.rename
(depuis 1.3.0) exécuté après le déplacement d'un fichier, paramètre idem, avec un paramètrenew_path
supplémentaire pointant sur le chemin de destinationfile.create
(depuis 1.3.0) exécuté après la création d'un nouveau fichier non-videfile.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 corbeillefile.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é, sinonNULL
), etfile
(contenant une entitéFile
). RenvoyerTRUE
interrompt l'exécution.http.request.file.after
(depuis 1.1.11) : identique, mais exécuté APRÈS le traitement de la requête, renvoyerTRUE
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) etpage
qui contient une entitéPage
ouNULL
si l'URI fournie ne correspond à aucune page connue. RenvoyerTRUE
interrompt l'exécution.web.request.after
(depuis 1.3.0) : identique, mais exécuté APRÈS le traitement de la requête, renvoyerTRUE
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ètrestring
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, ettarget
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 renvoieTRUE
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 destinataireemail.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 constanteUSE_CRON
dans la configuration et d'exécuter le scriptwww/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'accueilweb.page.version.new
(depuis 1.3.0) appelé après la modification du contenu d'une page web (nouvelle version du texte)