Catégorie

Extensions & Plugins

Système de plugins à hooks, plugins bundlés (AI Content, Cookie Notice) et API d'extension.

Le système d'extensions de QuietCMS suit la même philosophie que le reste du CMS : la puissance par la simplicité. Un plugin minimal tient en deux fichiers et une fonction PHP. Il n'y a pas de classe abstraite à étendre, pas d'interface à implémenter, pas de système d'injection de dépendances à maîtriser. Si vous savez écrire une fonction PHP, vous savez écrire un plugin QuietCMS.

Les plugins sont activés et désactivés depuis le back-office en un clic. Ils sont chargés uniquement s'ils sont actifs, ce qui garantit qu'un plugin inactif n'a strictement aucun impact sur les performances du site. Le répertoire plugins/ peut contenir autant de plugins que nécessaire — seuls les plugins activés sont inclus dans le cycle de rendu.

Architecture d'un plugin — deux fichiers suffisent

La structure minimale d'un plugin QuietCMS est la suivante :

plugins/
└── mon-plugin/
    ├── plugin.json   # Métadonnées et déclaration des hooks
    └── index.php    # Implémentation des fonctions de hook

Le fichier plugin.json déclare les métadonnées du plugin (nom, version, auteur, description) et la liste des hooks qu'il implémente. Le fichier index.php contient les fonctions qui seront appelées pour chaque hook.

// plugin.json
{
  "name": "Mon Plugin",
  "version": "1.0.0",
  "author": "Mon Nom",
  "description": "Description courte du plugin.",
  "hooks": ["body_end", "head_end"]
}

Les hooks disponibles

QuietCMS expose actuellement deux points d'injection dans le cycle de rendu :

  • body_end — Exécuté juste avant la balise de fermeture </body>. C'est le hook idéal pour injecter des scripts JavaScript, des trackers analytics, des widgets de chat, des bannières de cookies ou tout autre contenu à charger en dernier sur la page.
  • head_end — Exécuté juste avant la balise de fermeture </head>. Utilisez ce hook pour injecter des feuilles de style CSS supplémentaires, des polices, des balises <meta> spécifiques au plugin ou des scripts à charger en priorité.

La convention de nommage des fonctions de hook est : plugin_{slug}_{hook}(array $args): void. Pour un plugin avec le slug mon-plugin et le hook body_end, la fonction sera plugin_mon_plugin_body_end(array $args): void (les tirets sont convertis en underscores).

Exemple complet — plugin de tracking analytics

<?php
// plugins/analytics-custom/index.php

function plugin_analytics_custom_head_end(array $args): void {
    $trackingId = $args['settings']['analytics_custom_id'] ?? '';
    if (!$trackingId) return;
    ?>
    <script async src="https://www.googletagmanager.com/gtag/js?id=<?= htmlspecialchars($trackingId) ?>"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', '<?= htmlspecialchars($trackingId) ?>');
    </script>
    <?php
}

function plugin_analytics_custom_body_end(array $args): void {
    // Rien à faire dans body_end pour cet exemple
}

Plugin bundlé — AI Content Generator

QuietCMS intègre un plugin de génération de contenu par intelligence artificielle directement disponible à l'installation. Ce plugin ajoute un bouton dans l'éditeur de pages et d'articles qui ouvre un panneau de génération : saisissez un sujet ou des instructions, choisissez un fournisseur d'IA parmi les huit supportés, et le contenu HTML généré est inséré dans l'éditeur.

Les fournisseurs supportés sont : OpenAI GPT-4o, Anthropic Claude, Mistral, Groq, Together AI, Ollama (inférence locale, sans clé API ni coût), OpenRouter et DeepSeek. Les clés API sont stockées chiffrées en AES-256-GCM dans settings.json — elles ne transitent jamais en clair dans les réponses HTTP.

Plugin bundlé — Cookie Notice RGPD

Le plugin Cookie Notice fournit une bannière de consentement entièrement configurable depuis le back-office, sans modifier aucun fichier de thème. Personnalisez le texte du message, le libellé du bouton d'acceptation, la couleur de fond et la couleur du texte, et choisissez la position (bas ou haut de la page).

Lorsque le visiteur accepte, un cookie qcms_cookie_consent=1 est positionné pour 365 jours. La bannière disparaît pour les visites suivantes. Le plugin est injecté via le hook body_end — il fonctionne avec n'importe quel thème, actuel ou futur, sans aucune modification.

Étendre les hooks — ajouter des points d'injection

Les deux hooks natifs couvrent la grande majorité des cas d'usage. Mais si votre plugin a besoin de s'injecter dans un endroit précis du cycle de rendu (par exemple, après l'en-tête du thème, ou avant la zone de commentaires), vous pouvez ajouter vos propres appels à PluginManager::doAction('mon_hook') dans les fichiers de templates de votre thème. Ces hooks personnalisés sont immédiatement disponibles pour tous les plugins qui les déclarent dans leur plugin.json.

Développer des extensions de qualité

Les articles de cette catégorie détaillent chaque aspect du système d'extension de QuietCMS. Voici les principes fondamentaux à respecter pour créer des plugins robustes, performants et faciles à maintenir.

Toujours vérifier les données reçues dans $args

Le tableau $args passé à chaque fonction de hook contient les données du contexte courant : $args['page'] (données de la page en cours de rendu), $args['settings'] (configuration globale), $args['template'] (type de template : page, post, home, category…). Vérifiez systématiquement l'existence des clés avec ?? avant de les utiliser — un hook peut être appelé dans des contextes variés.

Stocker la configuration dans settings.json

Un plugin qui a besoin de configuration (clé API, identifiant de tracker, couleur de bannière…) doit utiliser les paramètres de QuietCMS stockés dans settings.json. Ajoutez vos options dans les réglages du back-office via l'API AdminManager. Les données sensibles (clés API) doivent être chiffrées avec Security::encrypt() avant stockage et déchiffrées avec Security::decrypt() à la lecture.

Ne pas modifier le core

La règle d'or du développement de plugins QuietCMS : ne modifiez jamais les fichiers du répertoire core/. Toute modification du core sera écrasée lors d'une mise à jour de QuietCMS. Si une fonctionnalité que vous souhaitez implémenter nécessite une modification du core, utilisez le mécanisme de hooks existant ou proposez l'ajout d'un nouveau hook via une pull request sur le dépôt officiel.

Performance — charger uniquement le nécessaire

Un plugin bien conçu vérifie d'abord si son exécution est nécessaire avant de faire quoi que ce soit. Par exemple, un plugin de widget de chat ne doit pas injecter ses scripts sur les pages d'erreur 404 ou sur les archives. Utilisez $args['template'] pour conditionner l'exécution du hook au type de page concerné.

Distribuer un plugin

Un plugin QuietCMS est autonome par conception — il suffit de copier le répertoire plugins/{slug}/ vers l'installation cible et d'activer le plugin depuis le back-office. Pour distribuer un plugin, publiez l'archive du répertoire (zip ou tar.gz) avec un README.md qui documente les paramètres de configuration et les hooks implémentés. La simplicité de la structure rend la distribution et l'installation triviales, sans gestionnaire de paquets.

Cycle de vie et chargement des extensions

Une extension QuietCMS se résume à un dossier contenant, au minimum, un fichier de description et un fichier d'implémentation. Au démarrage de l'application, le gestionnaire de plugins parcourt le répertoire des extensions, lit leur description et n'active que celles qui sont marquées comme activées. Ce chargement paresseux garantit qu'une extension désactivée n'a strictement aucun coût à l'exécution : son code n'est pas inclus, ses hooks ne sont pas enregistrés, son éventuelle configuration n'est pas lue.

Cette discipline de chargement a une conséquence directe sur la performance et la fiabilité. Activer dix extensions dont une seule est utile sur une page donnée ne pénalise pas le rendu, car chaque extension ne s'exécute qu'aux points d'ancrage qui la concernent. Le modèle encourage ainsi une composition fine : on active précisément ce dont on a besoin, et l'on désactive proprement le reste sans laisser de code mort résider dans l'application.

Isolation et robustesse des extensions

Le principe directeur du système d'extensions est qu'un plugin ne doit jamais pouvoir compromettre la stabilité du noyau. Les hooks reçoivent des données encadrées, et il incombe à chaque extension de valider ce qu'elle reçoit avant de l'utiliser. Une extension bien écrite vérifie la présence et le type des arguments qui lui sont transmis, échappe systématiquement les données qu'elle réinjecte dans une page, et échoue silencieusement plutôt que de provoquer une erreur fatale lorsqu'une condition n'est pas remplie.

Le fait qu'un plugin ne modifie jamais directement le cœur de l'application est une règle structurante. Toute la personnalisation passe par les points d'extension prévus à cet effet, ce qui signifie qu'une mise à jour du noyau ne casse pas les extensions correctement conçues. Cette séparation nette entre le cœur stable et les extensions évolutives est ce qui permet à un site de gagner en fonctionnalités sans accumuler de dette technique ni se rendre dépendant d'une version figée du système.

Configuration persistante et réglages

Fidèle au modèle flat-file, une extension stocke ses réglages dans un fichier JSON dédié plutôt que dans une base de données. Cette approche présente plusieurs avantages concrets : la configuration est versionnable, sauvegardable et migrable au même titre que le reste du site, et elle reste lisible par un humain. Lorsqu'on déplace un site d'un environnement à un autre, les réglages des extensions suivent naturellement, sans procédure d'export-import spécifique.

Cette persistance transparente facilite aussi le débogage. En cas de comportement inattendu, un administrateur peut inspecter directement le fichier de configuration d'une extension pour comprendre son état, voire le corriger manuellement. Là où un réglage perdu au fond d'une table de base de données est difficile à retrouver, un fichier JSON nommé explicitement reste immédiatement accessible.

Marketplaces, distribution et partage

Les extensions peuvent être regroupées et distribuées sous forme de paquets, ce qui ouvre la voie à un écosystème de partage. Un développeur qui a conçu une extension utile peut l'empaqueter et la mettre à disposition d'autres utilisateurs, qui l'installent en déposant simplement le dossier au bon endroit puis en l'activant depuis le back-office. Cette simplicité de distribution est cohérente avec la philosophie du projet : pas de gestionnaire de paquets obligatoire, pas de compilation, pas de dépendances en cascade à résoudre.

Pour distribuer une extension de qualité, quelques principes font la différence : documenter clairement les hooks utilisés et les réglages disponibles, prévoir des valeurs par défaut raisonnables pour que l'extension fonctionne dès l'activation, et tester le comportement lorsque l'extension est désactivée puis réactivée. Une extension qui respecte ces conventions s'intègre sans friction et inspire confiance à ceux qui l'adoptent.

Des cas d'usage variés

Le système d'extensions couvre un large éventail de besoins. Certaines extensions enrichissent le front : bannière de consentement aux cookies conforme au RGPD, intégration d'outils d'analyse, widgets de partage social. D'autres assistent la production de contenu, comme la génération assistée par intelligence artificielle. D'autres encore agissent en coulisses, en ajoutant des en-têtes, en transformant des sorties ou en connectant le site à des services externes via les points d'injection prévus.

Cette polyvalence tient à la conception même des hooks, qui exposent les moments clés du cycle de vie d'une requête — génération de l'en-tête, rendu du contenu, pied de page — sans imposer de forme particulière à ce qu'une extension en fait. Le développeur dispose ainsi d'une grande liberté tout en restant dans un cadre prévisible, ce qui est précisément l'équilibre recherché par un bon système d'extensibilité.

Compatibilité et maintenance dans la durée

La pérennité d'un site repose en partie sur la stabilité de ses extensions. Parce que QuietCMS sépare nettement le noyau des plugins et que ces derniers communiquent avec le cœur uniquement via des hooks documentés, le risque de rupture lors d'une mise à jour est faible. Une extension qui n'altère pas le code central et qui valide ses entrées continue de fonctionner d'une version à l'autre, sans nécessiter de réécriture à chaque évolution du système.

Cette stabilité contractuelle entre le noyau et les extensions est un atout souvent sous-estimé. Elle permet d'investir dans le développement d'une extension sans craindre qu'elle ne devienne obsolète au moindre changement, et elle protège les sites en production contre les régressions surprises. En traitant l'extensibilité comme un contrat clair plutôt que comme un accès libre au code, QuietCMS rend son écosystème à la fois ouvert et durable.

Étendre sans alourdir : la juste mesure

La force d'un système d'extensions ne se mesure pas au nombre de plugins installés mais à la pertinence de chacun. QuietCMS encourage une approche frugale : ajouter une extension pour répondre à un besoin réel, et la retirer dès qu'elle ne sert plus. Chaque extension active représente du code supplémentaire à charger, à maintenir et à surveiller du point de vue de la sécurité ; la désactiver proprement, sans laisser de résidus, fait partie d'une bonne hygiène d'exploitation.

Cette modération est cohérente avec l'esprit général du projet, qui préfère un cœur léger et des ajouts ciblés à une plateforme tentaculaire. En offrant un mécanisme d'extension simple, isolé et documenté, QuietCMS permet à chaque site de grandir exactement dans la direction dont il a besoin, sans hériter de fonctionnalités inutiles ni des risques qui les accompagnent. L'extensibilité y est conçue comme un moyen d'adapter l'outil à son usage, et non comme une fin en soi.