Chapitre 01 — Panorama des types de plugins
Bienvenue dans la Partie 6 de cette documentation, celle qui vous fera passer du statut d’utilisateur avancé de Moodle à celui de développeur de plugins. Dans les parties précédentes, vous avez exploré l’architecture générale de Moodle 5.2, ses APIs internes, sa base de données et son système de rendu. Vous savez désormais comment Moodle fonctionne. Cette partie répond à la question suivante : comment étendre Moodle proprement, sans jamais toucher au code du cœur ?
La réponse tient en un mot : les plugins. Mais avant d’écrire la moindre ligne de code, vous devez répondre à une question que tout développeur Moodle se pose au début de chaque projet, et qui conditionne tout le reste : quel type de plugin dois-je créer ? Un mauvais choix à cette étape se paie cher — un plugin local qui aurait dû être un module d’activité ne sera jamais notable dans le carnet de notes ; un bloc qui aurait dû être un rapport n’apparaîtra jamais au bon endroit dans la navigation d’administration.
Ce chapitre est donc un chapitre de cartographie. Nous allons dresser le panorama complet des types de plugins de Moodle 5.2 (il y en a plus de soixante), zoomer sur les six types que vous utiliserez dans 90 % des cas, vous donner un arbre de décision pour choisir le bon type, et démonter le mécanisme des sous-plugins, une spécificité de Moodle qui n’a pas vraiment d’équivalent dans l’écosystème JavaScript.
💡 Pour un dev React : si vous cherchez une analogie, un « type de plugin » Moodle est à mi-chemin entre un package npm et une interface TypeScript. Comme un package, il est versionné, installable et désinstallable. Comme une interface, il impose un contrat strict : des fichiers obligatoires à des emplacements précis, des classes à implémenter, des conventions de nommage. La grande différence avec npm : le code du plugin est déployé dans l’arborescence de Moodle (pas dans un
vendor/ounode_modules/isolé), et c’est l’emplacement du dossier qui détermine le type du plugin.
1. « Tout est plugin » : la philosophie de Moodle
1.1 Une architecture radicalement modulaire
Moodle applique de façon presque dogmatique le principe « everything is a plugin ». Le forum que vos utilisateurs connaissent ? C’est le plugin mod_forum. La méthode de connexion par identifiant/mot de passe ? Le plugin auth_manual. L’inscription manuelle des étudiants dans un cours ? enrol_manual. Le thème par défaut ? theme_boost. Le format de cours par sections ? format_topics. Même l’éditeur de texte riche (editor_tiny), le lecteur vidéo (media_videojs), l’export CSV (dataformat_csv) et l’antivirus (antivirus_clamav) sont des plugins.
Le « cœur » de Moodle (tout ce qui vit dans lib/, sous les composants core et core_*) fournit les fondations : APIs, base de données, gestion des utilisateurs, permissions, rendu. Tout le reste — c’est-à-dire à peu près tout ce que voit l’utilisateur final — est implémenté sous forme de plugins, y compris par l’équipe Moodle HQ elle-même. Les plugins livrés avec Moodle sont appelés plugins standard (standard plugins) ; ceux que vous installez ou développez sont des plugins additionnels (additional/contrib plugins). Techniquement, ils sont strictement identiques : mêmes contrats, mêmes APIs, mêmes emplacements.
Cette architecture a une conséquence pédagogique majeure pour vous : le code du cœur est votre meilleure documentation. Quand vous développerez votre premier module d’activité, vous aurez sous les yeux une vingtaine de modules standard (mod/forum, mod/assign, mod/quiz…) écrits selon les mêmes règles que celles qu’on vous impose.
1.2 Qu’est-ce qu’un type de plugin, exactement ?
Un type de plugin (plugin type) est la combinaison de trois choses :
- Un contrat de fichiers : un emplacement d’installation imposé (par exemple
/mod/pour les activités,/blocks/pour les blocs), des fichiers obligatoires (version.phptoujours, souventlang/en/xxx.php, parfois une classe précise dansclasses/), et des fichiers optionnels conventionnels (db/install.xml,db/access.php,db/hooks.php,settings.php,lib.php…). - Un ensemble d’APIs et de points d’entrée que Moodle appellera automatiquement : classes à étendre (par exemple
block_basepour un bloc,\core_ai\providerpour un fournisseur d’IA), fonctions de callback attendues, hooks écoutables. - Un cycle de vie géré par le cœur : installation (création des tables déclarées dans
db/install.xml), mise à jour (db/upgrade.php), désinstallation (avec nettoyage), gestion des dépendances et des versions viaversion.php.
Le type détermine donc ce que votre plugin a le droit de faire et où Moodle ira le chercher. Un bloc ne peut pas déclarer d’élément notable dans le carnet de notes ; un filtre ne peut pas afficher de page autonome dans un cours ; un thème ne peut pas définir de web service. Chaque type expose un sous-ensemble des capacités de la plateforme — et c’est précisément pour cela que le choix du type est la première décision d’architecture de tout projet Moodle.
⚠️ Piège : contrairement à un framework comme Next.js où la structure de dossiers est votre affaire à l’intérieur de conventions souples, chez Moodle le contrat de fichiers est rigide et vérifié. Un fichier
db/access.phpmal nommé (db/acces.php) ne provoquera aucune erreur : il sera simplement ignoré, et vos capabilities n’existeront jamais. Moodle échoue souvent silencieusement sur les fichiers de convention. Prenez l’habitude de vérifier vos créations via Administration du site > Développement > Purger les caches puis la page des notifications d’administration.
1.3 Rappel : le frankenstyle type_nom
Vous avez déjà rencontré le frankenstyle dans la Partie 1, mais il est si central ici qu’un rappel s’impose. Chaque plugin possède un nom de composant unique de la forme :
{type}_{nom}typeest le préfixe du type de plugin :mod,block,local,theme,auth,enrol,qtype,tool,aiprovider…nomest le nom du plugin, en minuscules, commençant par une lettre, composé uniquement de lettres, chiffres et underscores (les underscores sont tolérés mais déconseillés, car ils compliquent le parsing du frankenstyle).
Exemples : mod_forum, block_html, local_mytools, theme_boost, qtype_multichoice, aiprovider_openai, tiny_recordrtc.
Ce nom frankenstyle irrigue absolument tout : le namespace PHP des classes (\local_mytools\external\get_data), les noms de tables (local_mytools_items), les clés de chaînes de langue (get_string('pluginname', 'local_mytools')), les noms de capabilities (local/mytools:manage — notez le / au lieu du _ dans ce cas précis), les identifiants de templates Mustache (local_mytools/main_page), les modules AMD JavaScript (local_mytools/app).
⚠️ Piège : les modules d’activité (
mod) sont l’exception historique du frankenstyle. Le composant s’appelle bienmod_forum, mais le fichier de langue estlang/en/forum.php(sans le préfixemod_), les tables se nommentforum,forum_posts(sans préfixemod_), etget_string('pluginname', 'forum')fonctionne aussi bien queget_string('pluginname', 'mod_forum'). Aucun autre type de plugin ne bénéficie de cette exception : pour un bloc, le fichier de langue est bienlang/en/block_monbloc.php. Cette asymétrie est une source d’erreurs classique quand on passe de son premiermodà son premierblock.
1.4 Où les plugins s’installent physiquement
Chaque type de plugin possède un dossier racine imposé dans l’arborescence de Moodle. C’est le dossier qui fait le type : un plugin déposé dans /blocks/ est un bloc, un plugin déposé dans /local/ est un plugin local. Il n’y a pas de fichier de manifeste qui déclarerait « je suis un bloc » — l’emplacement est la déclaration.
Point crucial pour Moodle 5.2 : depuis Moodle 5.1, la racine web a été déplacée dans le sous-dossier public/. Concrètement, l’arborescence d’une installation 5.2 ressemble à ceci :
moodle/ ← racine du projet (dirroot au sens git)
├── public/ ← racine web (DocumentRoot) et racine du code applicatif
│ ├── admin/
│ │ └── tool/ ← plugins « tool » (outils d'administration)
│ ├── auth/ ← plugins d'authentification
│ ├── blocks/ ← blocs
│ ├── course/
│ │ └── format/ ← formats de cours
│ ├── lib/ ← bibliothèques du cœur
│ ├── local/ ← plugins locaux
│ ├── mod/ ← modules d'activité
│ ├── theme/ ← thèmes
│ └── ... (tous les autres dossiers de plugins)
├── config.php ← hors de la racine web depuis 5.1
└── vendor/ / node_modules/ ← dépendances de build, jamais serviesDans toute la documentation officielle (et dans ce cours), les chemins de plugins sont donnés relativement à la racine du code : quand nous écrivons « un bloc s’installe dans /blocks/xxx », comprenez public/blocks/xxx dans une installation 5.1+. La constante PHP $CFG->dirroot pointe vers ce dossier public/, donc le code lui-même ($CFG->dirroot . '/blocks/xxx') reste inchangé.
L’installation d’un plugin se fait de trois façons :
- Par l’interface web : Administration du site > Plugins > Installer des plugins, en téléversant un ZIP (nécessite que le serveur web ait le droit d’écrire dans le dossier cible — souvent désactivé en production).
- Par dépôt de fichiers : décompresser/cloner le plugin dans le bon dossier, puis visiter la page de notifications d’administration (ou lancer
php admin/cli/upgrade.php). C’est la méthode standard en déploiement automatisé (git submodules, scripts CI, images Docker). - Via le répertoire officiel : recherche et installation en un clic depuis moodle.org, si le site est configuré pour.
💡 Pour un dev React : il n’y a pas de
npm installchez Moodle. Le plugin est copié dans l’arborescence, puis Moodle détecte au chargement de la page d’administration qu’un dossier contient unversion.phpinconnu ou plus récent que la version enregistrée en base, et déclenche le processus d’installation/mise à jour (création de tables, enregistrement des capabilities, etc.). L’équivalent mental le plus proche est un système de migrations déclenché par la présence de fichiers. Corollaire : supprimer le dossier ne désinstalle pas le plugin — les tables et données restent, et Moodle signalera un plugin « manquant en base ». La désinstallation propre passe par l’interface d’administration (ouphp admin/cli/uninstall_plugins.php).
📚 Aller plus loin : la page de référence absolue pour ce chapitre est https://moodledev.io/docs/5.2/apis/plugintypes , qui liste tous les types avec un lien vers la documentation dédiée de chacun. Gardez-la en favori : c’est la table des matières du développement Moodle.
2. Le catalogue complet des types de plugins de Moodle 5.2
Voici maintenant le panorama exhaustif, organisé par grandes familles fonctionnelles. Pour chaque type : son nom, son préfixe frankenstyle, son dossier d’installation (relatif à la racine du code, donc sous public/ en 5.1+), son cas d’usage en une phrase, et un exemple tiré du cœur de Moodle.
2.1 Activités et contenu de cours
C’est la famille reine : tout ce que les enseignants ajoutent et manipulent dans leurs cours.
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Module d’activité | mod | /mod/xxx | Une activité ou ressource ajoutée dans un cours, potentiellement notable et suivie par étudiant. | mod_forum, mod_assign, mod_quiz |
| Format de cours | format | /course/format/xxx | La mise en page globale d’un cours : comment les sections et activités sont organisées et affichées. | format_topics, format_weeks |
| Condition d’accès | availability | /availability/condition/xxx | Une condition de restriction d’accès aux activités et sections (« visible si… »). | availability_date, availability_grade |
| Champ personnalisé | customfield | /customfield/field/xxx | Un type de champ pour les champs personnalisés (de cours notamment) : texte, menu, nombre… | customfield_select, customfield_number |
| Type de contenu (banque de contenus) | contenttype | /contentbank/contenttype/xxx | Un type de contenu créable/déposable dans la banque de contenus. | contenttype_h5p |
| Filtre de texte | filter | /filter/xxx | Une transformation automatique du texte affiché (liens automatiques, notation mathématique, multilangue…). | filter_mathjaxloader, filter_multilang |
| Bibliothèque H5P | h5plib | /h5p/h5plib/xxx | Le packaging d’une version de la bibliothèque H5P officielle intégrée à Moodle (rarement développé par des tiers). | h5plib_v127 (la version évolue) |
À noter : le terme courseformat que vous croiserez parfois désigne le sous-système du cœur core_courseformat (les APIs de rendu de cours introduites en 4.0) ; le type de plugin, lui, s’appelle bien format avec le frankenstyle format_xxx.
2.2 Sous-plugins des activités du cœur
Ces types sont des sous-plugins : ils n’existent que parce qu’un module d’activité du cœur les déclare (nous détaillerons le mécanisme en section 5). Vous les développerez pour étendre mod_assign, mod_quiz, etc.
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Type de remise de devoir | assignsubmission | /mod/assign/submission/xxx | Une façon pour l’étudiant de remettre son travail dans un devoir. | assignsubmission_file, assignsubmission_onlinetext |
| Type de feedback de devoir | assignfeedback | /mod/assign/feedback/xxx | Une façon pour l’enseignant de rendre un feedback dans un devoir. | assignfeedback_comments, assignfeedback_editpdf |
| Règle d’accès au test | quizaccess | /mod/quiz/accessrule/xxx | Une condition contrôlant qui peut tenter un test et dans quelles conditions (mot de passe, navigateur sécurisé…). | quizaccess_password, quizaccess_seb |
| Rapport de test | quiz | /mod/quiz/report/xxx | Un rapport d’analyse des tentatives d’un test. | quiz_statistics, quiz_overview |
| Outil de livre | booktool | /mod/book/tool/xxx | Un outil complémentaire pour l’activité Livre (impression, export…). | booktool_print, booktool_exportimscp |
| Champ de base de données | datafield | /mod/data/field/xxx | Un type de champ pour l’activité Base de données. | datafield_text, datafield_picture |
| Préréglage de base de données | datapreset | /mod/data/preset/xxx | Un modèle prédéfini complet pour l’activité Base de données. | datapreset_imagegallery |
| Rapport de forum | forumreport | /mod/forum/report/xxx | Un rapport d’analyse pour l’activité Forum. | forumreport_summary |
| Rapport SCORM | scormreport | /mod/scorm/report/xxx | Un rapport d’analyse des tentatives SCORM. | scormreport_basic |
| Formulaire d’atelier | workshopform | /mod/workshop/form/xxx | Une stratégie d’évaluation (grille) pour l’activité Atelier. | workshopform_rubric |
| Allocation d’atelier | workshopallocation | /mod/workshop/allocation/xxx | Une méthode d’attribution des travaux à évaluer entre pairs. | workshopallocation_random |
| Évaluation d’atelier | workshopeval | /mod/workshop/eval/xxx | Le calcul de la note d’évaluation dans l’Atelier. | workshopeval_best |
| Source LTI | ltisource | /mod/lti/source/xxx | Un traitement personnalisé des lancements LTI (type rarement utilisé). | (pas de plugin standard) |
| Service LTI | ltiservice | /mod/lti/service/xxx | L’implémentation d’un service de la spécification IMS LTI (notes, memberships…). | ltiservice_gradebookservices |
⚠️ Piège : le préfixe des rapports de test est
quiz_tout court (ex.quiz_statistics), et nonquizreport_. C’est un héritage historique unique en son genre — toutes les autres familles de rapports d’activité utilisent un préfixe explicite (forumreport_,scormreport_). Ne cherchez pas la logique, apprenez l’exception.
2.3 Questions et banque de questions
L’écosystème d’évaluation par questions possède ses propres types, tous très actifs dans la communauté.
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Type de question | qtype | /question/type/xxx | Un nouveau type de question (choix multiple, glisser-déposer, code…). | qtype_multichoice, qtype_essay |
| Comportement de question | qbehaviour | /question/behaviour/xxx | La façon dont l’étudiant interagit avec les questions pendant une tentative (feedback immédiat ou différé…). | qbehaviour_deferredfeedback |
| Format de question | qformat | /question/format/xxx | Un format d’import/export de questions. | qformat_gift, qformat_xml |
| Plugin de banque de questions | qbank | /question/bank/xxx | Une extension de l’interface de la banque de questions (colonnes, actions, filtres) — type introduit en 4.0. | qbank_comment, qbank_columnsortorder |
2.4 Évaluation et carnet de notes
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Rapport de notes | gradereport | /grade/report/xxx | Une vue d’affichage/édition du carnet de notes. | gradereport_grader, gradereport_singleview |
| Export de notes | gradeexport | /grade/export/xxx | Un format d’export des notes. | gradeexport_xls, gradeexport_ods |
| Import de notes | gradeimport | /grade/import/xxx | Un format d’import de notes. | gradeimport_csv |
| Méthode de notation avancée | gradingform | /grade/grading/form/xxx | Une interface de notation avancée utilisée dans les activités (grille critériée, guide…). | gradingform_rubric, gradingform_guide |
| Pénalité de note | gradepenalty | /grade/penalty/xxx | Une règle de pénalité automatique appliquée aux notes (retards…) — framework introduit avec Moodle 4.5/5.0. | gradepenalty_duedate |
2.5 Apparence, édition et médias
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Thème | theme | /theme/xxx | L’apparence complète du site : HTML des layouts, SCSS, templates Mustache surchargés. | theme_boost, theme_classic |
| Éditeur de texte | editor | /lib/editor/xxx | Un éditeur de texte riche alternatif pour les zones de saisie HTML. | editor_tiny, editor_textarea |
| Plugin TinyMCE | tiny | /lib/editor/tiny/plugins/xxx | Un bouton/fonctionnalité ajouté à l’éditeur TinyMCE (sous-plugin de editor_tiny). | tiny_recordrtc, tiny_link, tiny_premium |
| Plugin Atto | atto | /lib/editor/atto/plugins/xxx | Retiré du cœur. Extension de l’ancien éditeur Atto. | (voir encadré ci-dessous) |
| Lecteur multimédia | media | /media/player/xxx | Le rendu des liens audio/vidéo par un lecteur donné. | media_videojs, media_youtube |
⚠️ Piège : l’éditeur Atto et tous ses sous-plugins
atto_*ont été retirés du cœur dans Moodle 5.0 (MDL-77005) au profit de TinyMCE. En 5.2, le typeatton’existe donc plus dans une installation standard : si un site en a absolument besoin,editor_attoreste disponible comme plugin tiers maintenu sur le GitHub de Moodle HQ, mais toute nouvelle extension d’éditeur doit être un plugintiny_*. Si vous migrez un ancien pluginatto_*, c’est une réécriture (Atto était basé sur YUI, TinyMCE sur des modules AMD/ESM modernes), pas un portage.
💡 Pour un dev React : les plugins
tiny_*sont probablement les plus « front-end » de tout Moodle : l’essentiel du code est du JavaScript moderne (modules AMD compilés depuisamd/src/, souvent écrits en ES2015+) qui s’enregistre dans l’API de plugins de TinyMCE 7. Si vous voulez commencer par un terrain familier, c’est un excellent type d’entraînement — la partie PHP y est minimale.
2.6 Accès, identité et inscription
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Authentification | auth | /auth/xxx | Une méthode de vérification d’identité à la connexion (SSO, LDAP, OAuth 2…). | auth_oauth2, auth_ldap, auth_manual |
| Inscription | enrol | /enrol/xxx | Une méthode d’inscription des utilisateurs dans les cours (manuelle, auto-inscription, cohorte, paiement…). | enrol_manual, enrol_self, enrol_fee |
| Facteur MFA | factor | /admin/tool/mfa/factor/xxx | Un facteur d’authentification multifacteur (sous-plugin de tool_mfa, intégré au cœur depuis 4.3). | factor_totp, factor_sms, factor_email |
| Champ de profil utilisateur | profilefield | /user/profile/field/xxx | Un type de champ pour les champs de profil utilisateur personnalisés. | profilefield_menu, profilefield_text |
2.7 Communication et messagerie
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Sortie de message | message | /message/output/xxx | Un canal de distribution des notifications Moodle (e-mail, mobile, web…). | message_email, message_airnotifier |
| Fournisseur de communication | communication | /communication/provider/xxx | L’intégration d’un service de communication externe lié aux cours (salles Matrix, lien visio…) — type introduit en 4.2. | communication_matrix, communication_customlink |
| Passerelle SMS | smsgateway | /sms/gateway/xxx | L’envoi de SMS via un fournisseur externe (utilisé notamment par factor_sms) — type introduit en 4.5. | smsgateway_aws, smsgateway_modica |
2.8 Administration, rapports et données
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Outil d’administration | tool | /admin/tool/xxx | Un outil destiné aux administrateurs du site, intégré au menu d’administration. | tool_usertours, tool_dataprivacy, tool_mfa |
| Rapport | report | /report/xxx | Un rapport consultable au niveau du site ou d’un cours (journaux, participation…). | report_log, report_participation |
| Magasin de journaux | logstore | /admin/tool/log/store/xxx | Un backend de stockage des événements de journalisation (sous-plugin de tool_log). | logstore_standard, logstore_database |
| Format de données | dataformat | /dataformat/xxx | Un format d’export pour les tableaux téléchargeables partout dans Moodle (CSV, XLSX, JSON, PDF…). | dataformat_csv, dataformat_excel |
| Type de calendrier | calendartype | /calendar/type/xxx | Un système calendaire alternatif pour l’affichage des dates. | calendartype_gregorian |
Un mot d’histoire : vous croiserez dans de vieux articles le type coursereport (/course/report). Il a été supprimé depuis Moodle 2.2 au profit du type unifié report, dont les plugins peuvent s’afficher aussi bien au niveau site qu’au niveau cours. Si vous trouvez un tutoriel qui parle de coursereport, fuyez : il date d’avant 2012.
2.9 Infrastructure et performance
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Magasin de cache | cachestore | /cache/stores/xxx | Un backend de stockage pour l’API de cache (MUC) : Redis, fichiers, mémoire… | cachestore_redis, cachestore_file |
| Verrou de cache | cachelock | /cache/locks/xxx | Une implémentation de verrouillage pour le cache. | cachelock_file |
| Moteur de recherche | search | /search/engine/xxx | Un backend d’indexation pour la recherche globale. | search_solr, search_simpledb |
| Antivirus | antivirus | /lib/antivirus/xxx | L’analyse antivirale des fichiers téléversés. | antivirus_clamav |
| Backend d’apprentissage automatique | mlbackend | /lib/mlbackend/xxx | Un processeur de prédiction pour l’API d’analytique (modèles prédictifs). | mlbackend_php, mlbackend_python |
| Convertisseur de fichiers | fileconverter | /files/converter/xxx | La conversion de documents entre formats (ex. DOCX → PDF pour l’annotation des devoirs). | fileconverter_unoconv, fileconverter_googledrive |
| Protocole de web service | webservice | /webservice/xxx | Un protocole de transport pour les web services (REST, SOAP…). | webservice_rest, webservice_soap |
| Service MNet | mnetservice | /mnet/service/xxx | Un service exposé dans un réseau MNet (fédération de Moodle, technologie vieillissante). | mnetservice_enrol |
Attention à une confusion fréquente : le type webservice sert à créer un nouveau protocole de transport (imaginez : GraphQL, gRPC). C’est rarissime. Ce que vous voudrez faire dans 99 % des cas — exposer de nouvelles fonctions d’API consommables en REST — ne demande pas de plugin webservice_* : n’importe quel plugin (typiquement un local_*) peut déclarer des fonctions externes dans db/services.php et des classes dans classes/external/. Nous y consacrerons un chapitre entier.
2.10 Intégrations et échanges avec l’extérieur
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Dépôt de fichiers | repository | /repository/xxx | Une source externe de fichiers accessible depuis le sélecteur de fichiers (Google Drive, S3, URL…). | repository_googledocs, repository_upload |
| Portfolio | portfolio | /portfolio/xxx | Une destination d’export des productions des étudiants vers un service externe. | portfolio_googledocs, portfolio_download |
| Plagiat | plagiarism | /plagiarism/xxx | L’envoi des travaux remis à un service externe de détection de similitudes. | (aucun dans le cœur ; ex. tiers : plagiarism_turnitin) |
| Passerelle de paiement | paygw | /payment/gateway/xxx | L’encaissement de paiements via un prestataire (utilisé par enrol_fee notamment). | paygw_paypal |
Précision de vocabulaire : il n’existe pas de type de plugin pay ou payment — le sous-système du cœur s’appelle core_payment, et les plugins qui s’y branchent sont les passerelles paygw_*. Quand un client vous demande « un plugin de paiement », il veut soit une passerelle paygw_* (nouveau prestataire), soit simplement la configuration de enrol_fee avec une passerelle existante.
2.11 Intelligence artificielle
La famille la plus récente, introduite avec le sous-système core_ai en Moodle 4.5 et considérablement étoffée depuis.
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Fournisseur d’IA | aiprovider | /ai/provider/xxx | Un connecteur vers un service d’IA externe (fine enveloppe autour de son API : génération de texte, d’images, résumé…). | aiprovider_openai, aiprovider_azureai |
| Placement d’IA | aiplacement | /ai/placement/xxx | Un point d’exposition des actions d’IA dans l’interface utilisateur (dans l’éditeur, dans un cours…). | aiplacement_editor, aiplacement_courseassist |
L’architecture est volontairement découplée : le provider sait parler à un modèle (OpenAI, Azure, Ollama…), le placement sait où et comment l’utilisateur déclenche une action (bouton dans TinyMCE, panneau d’assistance de cours). Entre les deux, le sous-système core_ai route les actions (générer du texte, résumer, générer une image…). Vous pouvez donc écrire un provider pour un LLM maison sans toucher à l’interface, ou un placement inédit qui profitera de tous les providers installés.
📚 Aller plus loin : la documentation du sous-système IA et de ses deux types de plugins est sur https://moodledev.io/docs/5.2/apis/plugintypes/ai , avec les pages dédiées Providers et Placements . C’est l’une des zones de Moodle qui évolue le plus vite : vérifiez toujours la version de la doc.
2.12 Le fourre-tout assumé
| Type | Préfixe | Dossier | Cas d’usage | Exemple du cœur |
|---|---|---|---|---|
| Plugin local | local | /local/xxx | Toute personnalisation qui ne rentre dans aucun autre type : pages autonomes, web services, observers d’événements, tâches planifiées, hooks. | (aucun dans le cœur — par définition) |
Le type local mérite son propre traitement détaillé : c’est l’objet de la section 3.1.
⚠️ Piège : ne déduisez jamais un préfixe frankenstyle du nom du dossier, ni l’inverse. Les pièges sont nombreux : le dossier
/blocks/(pluriel) donne le préfixeblock_(singulier) ; le dossier/cache/stores/donnecachestore_;/grade/grading/form/donnegradingform_;/mod/quiz/report/donnequiz_;/admin/tool/donnetool_. En cas de doute, la source de vérité dans le code est la méthode\core_component::get_plugin_types()(et le fichier de référence https://moodledev.io/docs/5.2/apis/plugintypes ).
3. Zoom sur les six types que vous utiliserez vraiment
Plus de soixante types, mais soyons honnêtes : en tant que développeur intégrateur dans une organisation, vous passerez l’essentiel de votre temps sur une poignée d’entre eux. Voici les six à connaître intimement.
3.1 local — le couteau suisse
Le plugin local est le type le plus permissif de Moodle : il n’impose presque aucun contrat au-delà du minimum universel (version.php + fichier de langue), et il a accès à la quasi-totalité des APIs. C’est le type que vous choisirez quand rien d’autre ne colle, c’est-à-dire très souvent. Cas d’usage typiques :
- Une ou plusieurs pages autonomes : un tableau de bord métier, un formulaire d’export spécifique, une page d’intégration avec votre SI. Vous créez simplement
local/monplugin/index.phpet la page vit à l’URLhttps://votresite/local/monplugin/index.php. - Des web services sur mesure : la façon canonique d’exposer une API REST à une application externe (votre front Next.js, une app mobile, un ETL) est un plugin local qui déclare des fonctions externes dans
db/services.phpet les implémente dansclasses/external/. - Des observers d’événements : réagir à « un utilisateur vient de terminer un cours » ou « une note a été modifiée » pour pousser l’information vers un système tiers, sans interface utilisateur du tout.
- Des hooks : depuis Moodle 4.3+, la façon moderne d’injecter du comportement dans le cœur (par exemple ajouter du HTML avant le footer, étendre la navigation) passe par des callbacks de hooks déclarés dans
db/hooks.php— un plugin local est l’hôte naturel de ces callbacks quand ils ne se rattachent à aucune fonctionnalité visible. - Des tâches planifiées (
classes/task/) : synchronisations nocturnes, purges, rapports envoyés par e-mail.
Arborescence minimale d’un plugin local avec une page et un web service :
local/mytools/ ← composant : local_mytools
├── version.php ← obligatoire : version, dépendances
├── index.php ← page autonome (optionnelle)
├── settings.php ← réglages d'administration (optionnel)
├── lang/
│ └── en/
│ └── local_mytools.php ← chaînes de langue (anglais obligatoire)
├── db/
│ ├── access.php ← capabilities
│ ├── services.php ← déclaration des fonctions externes
│ ├── hooks.php ← callbacks de hooks (Moodle 4.3+)
│ └── tasks.php ← tâches planifiées
├── classes/ ← autoload PSR-4 : \local_mytools\...
│ ├── external/
│ │ └── get_report_data.php ← \local_mytools\external\get_report_data
│ └── task/
│ └── sync_users.php
└── templates/
└── dashboard.mustache ← template Mustache : local_mytools/dashboard💡 Pour un dev React : si votre objectif est de construire un front découplé (Next.js) qui consomme Moodle en API, votre tout premier plugin sera presque certainement un
local_*exposant des web services. C’est l’équivalent moodlien d’un dossierapp/api/: pas d’UI Moodle, juste des endpoints typés, des contrôles de permissions et de la logique métier. Le chapitre sur les web services (plus loin dans cette partie) construit exactement cela.
⚠️ Piège : la permissivité du type
localest aussi son danger. Comme « tout marche » dans un plugin local, la tentation est grande d’y fourrer ce qui aurait dû être un bloc, un rapport ou un module. Le coût apparaît plus tard : pas d’intégration dans la navigation de cours, pas d’instance configurable par les enseignants, pas de sauvegarde/restauration avec les cours, pas de notes. Utilisezlocalpour ce qui est réellement transversal ou sans interface — pas comme raccourci pour éviter d’apprendre le contrat d’un type plus adapté.
3.2 block — le composant d’interface réutilisable
Un bloc est un rectangle de contenu que l’utilisateur (ou l’administrateur) peut ajouter dans les colonnes latérales d’un cours, sur le tableau de bord (Dashboard), sur la page d’accueil du site. Le contrat est simple : une classe principale block_monbloc (historiquement dans blocks/monbloc/block_monbloc.php) qui étend block_base et implémente au minimum init() et get_content().
Les blocs sont le type d’entrée idéal pour un premier développement : le contrat est court, le résultat est immédiatement visible, et l’on peut y mettre progressivement toutes les briques modernes (template Mustache pour le rendu, module AMD pour l’interactivité, web service pour charger des données en AJAX, réglages d’instance pour la configuration par l’enseignant).
blocks/mystats/ ← composant : block_mystats
├── version.php
├── block_mystats.php ← classe block_mystats extends block_base
├── edit_form.php ← formulaire de config d'instance (optionnel)
├── lang/en/block_mystats.php
├── db/access.php ← block/mystats:addinstance, :myaddinstance
├── classes/
│ └── external/ ← web services pour l'AJAX du bloc
├── templates/
│ └── content.mustache
└── amd/
├── src/main.js ← source ES6
└── build/main.min.js ← compilé (grunt)Points d’architecture à connaître : un bloc est instanciable (le même bloc peut être ajouté dix fois avec dix configurations différentes), chaque instance a un contexte propre (fils du contexte de la page), et deux capabilities dédiées contrôlent qui peut l’ajouter (addinstance sur les pages normales, myaddinstance sur le tableau de bord personnel).
💡 Pour un dev React : le bloc est ce qui ressemble le plus à un composant au sens React — une unité d’UI autonome, configurable par props (les réglages d’instance), placée dans des slots (les régions du thème). La différence fondamentale : le rendu initial est fait côté serveur en PHP/Mustache, et l’interactivité s’ajoute par-dessus en JavaScript (progressive enhancement), là où React part du client. Résistez à l’envie de faire un bloc qui n’est qu’un
<div id="root"></div>hydraté en JS : vous perdriez l’accessibilité, le cache de rendu et la cohérence du thème.
3.3 mod — le module d’activité, le type roi
Le module d’activité est le type le plus riche et le plus exigeant : c’est ce que les enseignants ajoutent dans les sections de leurs cours via le sélecteur d’activités. Choisissez mod quand votre fonctionnalité coche l’une de ces cases :
- chaque instance appartient à un cours et est créée/configurée par l’enseignant ;
- les étudiants interagissent avec elle et cette interaction doit être suivie (achèvement d’activité, journaux) ;
- elle peut produire une note dans le carnet de notes ;
- elle doit être sauvegardée et restaurée avec le cours.
En contrepartie, le contrat est le plus lourd de tous les types : table principale obligatoire portant le nom du module, fonctions de cycle de vie (xxx_add_instance(), xxx_update_instance(), xxx_delete_instance()) dans lib.php, déclaration des features supportées (FEATURE_GRADE_HAS_GRADE, FEATURE_COMPLETION_TRACKS_VIEWS…), pages view.php et mod_form.php, intégration backup/restore dans backup/moodle2/, et bien plus. Deux chapitres complets de cette partie y seront consacrés.
mod/pomodoro/ ← composant : mod_pomodoro
├── version.php
├── lib.php ← pomodoro_add_instance(), _supports()...
├── mod_form.php ← formulaire de création/édition
├── view.php ← la page de l'activité
├── index.php ← liste des instances du cours
├── lang/en/pomodoro.php ← SANS préfixe mod_ (exception historique)
├── db/
│ ├── install.xml ← table « pomodoro » obligatoire
│ ├── access.php ← mod/pomodoro:addinstance, :view
│ └── events.php / hooks.php
├── backup/moodle2/ ← sauvegarde/restauration
├── classes/
├── templates/
└── pix/
└── monologo.svg ← icône dans le sélecteur d'activités⚠️ Piège : ne commencez jamais votre apprentissage de Moodle par un
modécrit de zéro. Entre le backup/restore, l’API de complétion, l’API de notes et les conventions historiques, c’est plusieurs semaines de travail pour un résultat professionnel. Le chemin raisonnable : d’abord unlocal, puis unblock, puis unmodgénéré par squelette (voir section 6) que vous étofferez. Beaucoup de besoins exprimés comme « une nouvelle activité » sont en réalité couverts par un type de question (qtype), un plugin H5P ou un outil externe LTI — vérifiez avant de vous lancer.
3.4 theme — l’apparence (renvoi vers la Partie 8)
Le thème contrôle l’intégralité du rendu : layouts de pages, SCSS injecté par-dessus Bootstrap 5, surcharge de n’importe quel template Mustache du cœur ou des plugins, voire surcharge des renderers PHP. En pratique, en 2026, on ne crée presque jamais un thème ex nihilo : on crée un thème enfant de theme_boost qui hérite de tout et ne redéfinit que le nécessaire (variables SCSS de marque, quelques templates).
Nous ne détaillons pas davantage ici : la Partie 8 de cette documentation est entièrement consacrée au theming (héritage Boost, pipeline SCSS, surcharge de templates Mustache, renderers). Retenez simplement pour l’arbre de décision : si le besoin est « changer l’apparence », la réponse est thème enfant, pas un plugin d’un autre type qui injecterait du CSS par des moyens détournés.
3.5 tool vs local — la vraie différence
C’est LA question d’entretien Moodle. Les deux types sont techniquement quasi identiques : mêmes capacités (pages, web services, tâches, observers, hooks), même absence de contrat lourd. Les différences réelles sont au nombre de trois :
- L’emplacement :
/admin/tool/xxxcontre/local/xxx. Les URLs des pages d’un tool vivent sous/admin/tool/xxx/..., ce qui les place sémantiquement (et parfois au niveau des règles de proxy/pare-feu) dans la zone d’administration. - La sémantique : un
toolest par convention un outil pour les administrateurs du site — migration de données, audit, nettoyage, configuration avancée. Le cœur suit cette logique :tool_dataprivacy(RGPD),tool_usertours(visites guidées),tool_task(gestion des tâches),tool_mfa(authentification multifacteur). Unlocalest une personnalisation destinée potentiellement à tous les utilisateurs. - L’ordre d’exécution et l’intégration admin : les tools sont naturellement rattachés à l’arborescence du menu d’administration ; les plugins locaux, eux, sont traités en dernier dans plusieurs mécanismes du cœur (ce qui est parfois précisément la raison de choisir
local: pouvoir « avoir le dernier mot » sur la navigation ou les réglages).
Règle pratique : si votre plugin n’a de sens que pour un administrateur système et s’intègre au menu d’administration → tool. Si des enseignants ou étudiants y accèdent, ou s’il n’a pas d’UI du tout → local. Dans le doute, local : c’est le choix le plus neutre et le plus courant dans la communauté.
3.6 report — les rapports
Un plugin report (/report/xxx) fournit une page d’analyse de données qui s’accroche automatiquement au bon endroit : dans Administration du site > Rapports pour les rapports de niveau site, et/ou dans le menu secondaire d’un cours pour les rapports de niveau cours (c’est le plugin qui décide, via ses callbacks de navigation et ses capabilities, où il apparaît). Exemples du cœur : report_log (journaux), report_participation, report_outline, report_insights.
Choisissez report plutôt que local dès que votre besoin est « une page en lecture qui analyse des données existantes pour les enseignants ou les administrateurs » : vous gagnez l’intégration automatique dans la navigation, la convention d’URL (/report/xxx/index.php?course=N) et la lisibilité pour les autres développeurs. Notez aussi que depuis Moodle 4.0, le cœur fournit l’API Report Builder (core_reportbuilder), qui permet de construire des rapports colonnes/filtres/export très riches avec peu de code — un plugin report moderne s’appuie dessus au lieu de générer ses tableaux HTML à la main.
📚 Aller plus loin : chaque type vedette a sa page dédiée sur moodledev.io : Local plugins , Blocks , Activity modules , Themes , Admin tools , Report Builder . Lisez la page du type avant de générer le squelette : chacune liste les fichiers obligatoires et les pièges spécifiques.
4. Arbre de décision : quel type pour mon besoin ?
Voici la démarche que nous vous recommandons de dérouler systématiquement, question par question. Spoiler assumé : pour vos premiers développements, la réponse sera presque toujours local ou block — et c’est très bien ainsi.
4.1 Première question : est-ce vraiment un plugin qu’il vous faut ?
Avant tout code, éliminez les faux besoins de plugin :
- « Changer un libellé, une couleur, cacher un élément » → personnalisation de langue (Administration > Langue > Personnalisation), réglages du thème, ou quelques lignes de SCSS dans un thème enfant. Pas de plugin.
- « Ajouter des informations aux cours ou aux utilisateurs » → champs personnalisés de cours et champs de profil utilisateur, configurables dans l’interface. Un plugin (
customfield_*/profilefield_*) n’est nécessaire que si aucun type de champ existant ne convient. - « Modifier le comportement d’un plugin existant » → vérifiez d’abord ses réglages, ses hooks, ses propres sous-plugins. Ne forkez jamais un plugin du cœur.
- « Intégrer un outil pédagogique externe » → LTI (
mod_ltipréconfiguré) couvre l’immense majorité des cas sans une ligne de code. - « Du contenu interactif » → H5P via la banque de contenus, avant d’envisager un
mod.
⚠️ Piège : le pire anti-pattern Moodle est la modification directe du cœur (« je patche juste
course/view.php, c’est plus rapide »). Chaque mise à jour mineure de sécurité (il y en a tous les deux mois) écrasera ou entrera en conflit avec vos patchs. La règle absolue : le cœur est en lecture seule ; toute personnalisation vit dans un plugin ou un thème enfant. Si vous pensez avoir trouvé un cas impossible sans patcher le cœur, vous avez soit raté un hook, soit trouvé un vrai manque — proposez alors le hook en amont sur le tracker.
4.2 L’arbre de décision
Déroulez les questions dans l’ordre ; la première réponse « oui » donne le type.
1. Les enseignants doivent-ils l’ajouter dans un cours, et les étudiants interagir avec (activité suivie, potentiellement notée) ?
→ mod (module d’activité). Sous-question : si c’est « une nouvelle sorte de question dans les tests », c’est qtype ; si c’est « une nouvelle façon de remettre un devoir », c’est assignsubmission.
2. Est-ce un affichage compact, optionnel, positionné dans une colonne ou sur le tableau de bord ?
→ block. Un bloc peut aussi porter des mini-outils interactifs (via AJAX), pas seulement de l’affichage.
3. Est-ce une page d’analyse en lecture (site ou cours) pour enseignants/admins ?
→ report (pensez Report Builder).
4. Est-ce un outil réservé aux administrateurs du site (audit, migration, configuration) ?
→ tool.
5. Faut-il changer l’apparence (couleurs, layouts, templates) ?
→ theme enfant de Boost (Partie 8).
6. Faut-il transformer du texte au moment de son affichage (mots-clés → liens, notation spéciale → rendu) ?
→ filter.
7. Faut-il brancher une méthode de connexion / un SSO (SAML, OIDC, LDAP…) ?
→ auth — après avoir vérifié que auth_oauth2 + un issuer configuré ne suffit pas (Google, Microsoft, tout OIDC standard : zéro code).
8. Faut-il contrôler qui est inscrit aux cours et comment (synchro SI, règles métier, paiement) ?
→ enrol. Si c’est seulement « payer avec le prestataire X », c’est paygw.
9. Faut-il restreindre l’accès à des activités selon une condition inédite ?
→ availability.
10. Faut-il brancher une source de fichiers externe ? Exporter vers un service externe ? Détecter le plagiat ?
→ repository / portfolio / plagiarism.
11. Faut-il connecter un modèle d’IA ou exposer l’IA à un nouvel endroit de l’interface ?
→ aiprovider / aiplacement.
12. Faut-il un nouveau canal de notification, un facteur MFA, une passerelle SMS, un fournisseur de visio ?
→ message / factor / smsgateway / communication.
13. Rien de tout cela : page autonome, API pour un front externe, observer d’événements, tâche planifiée, glue métier ?
→ local. Le filet de sécurité universel.
La même logique en diagramme :
4.3 Trois études de cas éclair
« Le service formation veut un tableau de bord des inscriptions en retard, visible par les managers. » Page en lecture, données existantes, public : encadrement → report (avec Report Builder), ou block sur le tableau de bord si l’on veut que chaque manager l’ait sous les yeux sans naviguer. Pas local : la navigation et les permissions de report sont exactement faites pour ça.
« On veut que notre app mobile interne récupère la progression des apprenants. » Pas d’UI Moodle, exposition d’API → local avec fonctions externes + service REST. C’est le cas d’école du plugin local.
« Les enseignants veulent une activité “journal de bord” hebdomadaire, notée. » Ajoutée au cours, interaction étudiante, note → mod. Mais avant de coder : mod_assign réglé en remises multiples, ou un plugin existant du répertoire (il y a plusieurs plugins de journal maintenus) couvrent peut-être 100 % du besoin.
💡 Pour un dev React : votre réflexe de développeur front sera de penser « une page = une route = je la crée où je veux ». Chez Moodle, pensez plutôt « une page = un emplacement dans une hiérarchie existante » : la navigation, le fil d’Ariane, les permissions et le contexte (site ? cours ? module ?) découlent du type de plugin choisi. Choisir le type, c’est choisir où votre fonctionnalité existe dans le modèle mental des utilisateurs — pas seulement son URL.
5. Les sous-plugins : des plugins dans les plugins
5.1 Le concept
Certains plugins sont eux-mêmes extensibles par des plugins : on parle de sous-plugins (subplugins). Le plugin hôte définit un ou plusieurs types de sous-plugins, avec leur propre préfixe frankenstyle et leur propre dossier — à l’intérieur du dossier de l’hôte. Le cœur traite ensuite ces sous-plugins presque comme des plugins normaux : chacun a son version.php, ses chaînes de langue, ses classes PSR-4, son cycle d’installation/mise à jour.
Vous avez déjà croisé de nombreux sous-plugins dans les tableaux de la section 2 sans forcément le remarquer :
assignsubmission_*etassignfeedback_*sont des sous-plugins demod_assign;quizaccess_*etquiz_*(rapports) sont des sous-plugins demod_quiz;datafield_*etdatapreset_*sont des sous-plugins demod_data;tiny_*sont des sous-plugins deeditor_tiny;logstore_*sont des sous-plugins detool_log;factor_*sont des sous-plugins detool_mfa;workshopform_*,workshopallocation_*,workshopeval_*sont des sous-plugins demod_workshop.
Seuls certains types de plugins ont le droit de déclarer des sous-plugins : les modules d’activité (mod), les éditeurs (editor), les outils d’administration (tool) et les plugins locaux (local). Un bloc ou un thème ne peut pas avoir de sous-plugins.
5.2 La déclaration : db/subplugins.json
Le plugin hôte déclare ses types de sous-plugins dans le fichier db/subplugins.json. Le format a changé avec Moodle 5.0, et c’est un point de vigilance important si vous lisez du code ancien :
- Format historique (jusqu’à Moodle 4.5) : une clé
plugintypes, dont les valeurs sont des chemins relatifs à la racine de Moodle (ex."mod/assign/submission"). - Format moderne (Moodle 5.0+) : une clé
subplugintypes, dont les valeurs sont des chemins relatifs au dossier du plugin hôte (ex."submission").
Un plugin qui veut supporter à la fois Moodle ≤ 4.5 et Moodle 5.x doit déclarer les deux clés, avec des types identiques et des chemins cohérents entre eux. Voici le fichier réel (et exemplaire) de mod_assign :
// Fichier : mod/assign/db/subplugins.json
{
"subplugintypes": {
"assignsubmission": "submission",
"assignfeedback": "feedback"
},
"plugintypes": {
"assignsubmission": "mod/assign/submission",
"assignfeedback": "mod/assign/feedback"
}
}Lecture : mod_assign déclare deux types de sous-plugins. Les plugins de type assignsubmission s’installent dans le sous-dossier submission/ de mod/assign (donc mod/assign/submission/xxx, composant assignsubmission_xxx), et les assignfeedback dans mod/assign/feedback/.
Et voici ce que cela donne côté sous-plugin — chaque sous-plugin est une arborescence de plugin complète et autonome :
mod/assign/submission/snapshot/ ← composant : assignsubmission_snapshot
├── version.php ← version PROPRE au sous-plugin
│ $plugin->component = 'assignsubmission_snapshot';
├── locallib.php ← classe imposée par l'hôte (contrat d'assign)
├── lang/en/assignsubmission_snapshot.php
├── db/
│ ├── install.xml ← ses propres tables
│ └── upgrade.php
├── classes/ ← \assignsubmission_snapshot\...
└── settings.phpNotez la subtilité : le contrat de fichiers d’un sous-plugin est défini par le plugin hôte, pas par le cœur. C’est mod_assign qui exige que chaque assignsubmission_* fournisse une classe étendant assign_submission_plugin ; c’est editor_tiny qui exige que chaque tiny_* fournisse une classe plugininfo et un module AMD plugin. La page moodledev.io du plugin hôte documente ce contrat.
⚠️ Piège : dans
version.phpd’un sous-plugin, la propriété$plugin->componentdoit impérativement porter le frankenstyle complet du sous-plugin (assignsubmission_snapshot), pas celui de l’hôte. Autre point : déclarer une dépendance vers l’hôte dans$plugin->dependenciesest inutile pour les sous-plugins du cœur (l’hôte est toujours là), mais indispensable si l’hôte est lui-même un plugin additionnel — sinon l’installation du sous-plugin seul échouera de façon peu explicite.
5.3 Les conséquences du modèle
Le rattachement hôte/sous-plugin n’est pas qu’une affaire de dossiers ; il a des effets de cycle de vie bien réels :
- Désinstallation en cascade : désinstaller le plugin hôte désinstalle tous ses sous-plugins (et leurs données). Désinstaller
mod_assignemporterait tous lesassignsubmission_*etassignfeedback_*du site. - Versionnage indépendant : chaque sous-plugin a son propre
version.php, son propredb/upgrade.php, sa propre entrée dans la tableconfig_plugins. On met à jour un sous-plugin sans toucher à l’hôte. - Découverte par le cœur :
core_componentlit lessubplugins.jsonde tous les plugins installés pour construire la carte complète des types. C’est pourquoi vos classes\assignsubmission_snapshot\...sont autoloadées exactement comme celles d’un plugin de premier niveau. - Cache de composants : après ajout ou modification d’un
subplugins.json, purgez les caches — la carte des composants est agressivement mise en cache.
Quand vous développerez un plugin hôte (typiquement un local ou un mod complexe), demandez-vous si une partie de sa logique gagnerait à être un type de sous-plugin : c’est le mécanisme idéal pour offrir des points d’extension à d’autres équipes (« notre local_sync accepte des sous-plugins syncsource_*, chacun sachant parler à un SI différent »). C’est plus lourd à mettre en place qu’un simple hook, mais infiniment plus structurant quand les extensions ont besoin de tables, de réglages et de versions propres.
📚 Aller plus loin : le mécanisme complet est documenté sur https://moodledev.io/docs/5.2/apis/subplugins (format
subplugintypesde Moodle 5.0+, rétrocompatibilité, découverte). Pour voir un contrat de sous-plugin bien documenté côté hôte, lisez https://moodledev.io/docs/5.2/apis/plugintypes/assign/submission — la page décrit précisément les classes et callbacks quemod_assignattend de ses sous-plugins.
6. Où trouver des exemples et des squelettes
Terminons par vos trois meilleures sources pour ne jamais partir d’une page blanche.
6.1 Le code du cœur, votre référence permanente
Répétons-le : chaque type de plugin est livré avec des implémentations standard écrites par Moodle HQ, relues et maintenues depuis des années. Avant d’écrire un enrol, lisez enrol/self ; avant un filter, lisez filter/urltolink (court et limpide) ; avant un qtype, lisez question/type/shortanswer. Une technique efficace : choisissez le plugin standard le plus petit du type visé et prenez-le comme gabarit mental — les gros plugins (mod_quiz, mod_assign) sont d’excellentes références d’API mais de mauvais modèles de démarrage, car ils traînent quinze ans d’historique.
6.2 Le répertoire officiel des plugins
Le Moodle Plugins Directory (https://moodle.org/plugins/ ) recense des milliers de plugins tiers, filtrables par type et par version de Moodle supportée. Trois usages pour vous : vérifier qu’un besoin n’est pas déjà couvert (avant tout développement !), trouver des exemples de code récents pour un type donné (chaque fiche pointe vers le dépôt source, presque toujours GitHub), et jauger les pratiques de la communauté (un plugin approuvé dans le répertoire a passé une revue de code par l’équipe de validation, avec des critères publics de qualité et de sécurité). Filtrez toujours par « supports Moodle 5.2 » : un plugin non maintenu depuis la 3.x vous apprendra surtout de mauvaises habitudes (callbacks legacy, YUI, absence de templates).
6.3 Les générateurs de squelettes
Deux outils génèrent l’arborescence complète et conforme d’un plugin à partir de quelques réponses :
tool_pluginskel: un plugin admin tool (installable depuis le répertoire) qui offre une interface web et une CLI pour générer le squelette de la plupart des types —version.php, fichiers de langue,db/access.php, classes, tests PHPUnit, le tout correctement nommé. C’est l’outil recommandé par la documentation développeur.moosh(Moodle Shell, https://moosh-online.com ) : un couteau suisse en ligne de commande pour administrateurs et développeurs, dont la commandemoosh generate-module,moosh generate-block,moosh generate-local, etc., crée des squelettes en une ligne. Moosh rend par ailleurs mille autres services au quotidien (création de cours et d’utilisateurs de test, purge de caches, exécution de tâches).
# Exemple : générer un squelette de bloc avec moosh, depuis la racine du code
cd /var/www/moodle/public
moosh generate-block mystats
# → crée blocks/mystats/ avec la structure minimale conformeUn squelette généré n’est qu’un point de départ : relisez chaque fichier généré en le confrontant à la page moodledev.io du type, car les générateurs ont parfois un temps de retard sur les évolutions (hooks vs callbacks legacy, nouveau format subplugins.json…).
💡 Pour un dev React :
tool_pluginskeljoue le rôle d’uncreate-next-app— mais là oùcreate-next-appinstalle un projet autonome, le squelette Moodle s’insère dans une plateforme existante et doit être enregistré (visite de la page d’administration ouphp admin/cli/upgrade.php) avant d’exister aux yeux du système. Ajoutez ce réflexe à votre boucle de développement, avec la purge des caches (php admin/cli/purge_caches.php) : ce sont les deux commandes que vous taperez le plus souvent.
📚 Aller plus loin : le guide officiel de démarrage https://moodledev.io/general/development/gettingstarted et la checklist de contribution au répertoire https://moodledev.io/general/community/plugincontribution valent la lecture dès maintenant, même si vous ne publierez pas tout de suite : les critères d’approbation du répertoire constituent une excellente définition de « plugin de qualité professionnelle ».
L’essentiel à retenir
- Dans Moodle, tout est plugin ; le cœur ne fournit que les fondations. Les plugins standard et les vôtres obéissent aux mêmes règles.
- Un type de plugin = un dossier d’installation imposé + un contrat de fichiers + un ensemble d’APIs accessibles. Le composant frankenstyle
type_nomirrigue namespaces, tables, chaînes, capabilities et templates. - Depuis Moodle 5.1, la racine du code est
public/: les chemins documentés (/mod/xxx,/blocks/xxx…) s’entendent sous ce dossier. - Moodle 5.2 compte plus de soixante types, des plus classiques (
mod,block,theme,auth,enrol) aux plus récents (aiprovider,aiplacement,factor,communication,smsgateway,gradepenalty). Atto (atto_*) est retiré du cœur depuis 5.0 : l’extension d’éditeur moderne esttiny_*. - Pour choisir : dans un cours et suivi →
mod; affichage latéral →block; page d’analyse →report; outil d’admin →tool; apparence →themeenfant ; API/page/glue sans ancrage →local. Et souvent : pas de plugin du tout. - Les sous-plugins (déclarés dans
db/subplugins.json, clésubplugintypesen 5.x avec chemins relatifs au plugin hôte) permettent àmod,editor,tooletlocald’être eux-mêmes extensibles, avec versionnage indépendant et désinstallation en cascade. - Vos références : le code du cœur, le répertoire moodle.org/plugins, et les générateurs
tool_pluginskel/moosh.
Dans le chapitre suivant
Maintenant que vous savez quel plugin créer, il est temps d’apprendre à en construire un, fichier par fichier. Le chapitre 02 — Anatomie universelle d’un plugin dissèque la structure commune à tous les types : version.php et le versionnage, les fichiers du dossier db/ (install.xml, access.php, hooks.php, services.php, tasks.php…), l’autoloading PSR-4 du dossier classes/, les chaînes de langue, les templates Mustache et les modules AMD. C’est le socle sur lequel tous les chapitres suivants de cette partie s’appuieront.