Chapitre 12.3 — Pièges courants et FAQ
Partie 12 : Annexes — « Ça ne marche pas et je ne comprends pas pourquoi. » 9 fois sur 10, la réponse est ici. Classé par domaine. Version de référence : Moodle 5.2.
Top pièges du dev venant du monde JS/React
🔴 Caches (les 5 premiers à connaître)
1. « Mon changement ne s’applique pas. »
→ Purgez les caches : php admin/cli/purge_caches.php. Moodle cache les chaînes de langue, les templates Mustache, le JS compilé, la liste des plugins, la navigation. Le réflexe n°1.
2. « Ma chaîne de langue affiche [[maclé]]. »
→ Clé absente du fichier lang/en/, ou cache non purgé. Ajoutez la clé, purgez.
3. « Mon changement SCSS de thème est invisible. »
→ CSS de thème caché. En dev : $CFG->themedesignermode = true;. En prod : purge_caches. Et vérifiez le cache navigateur. Ne jamais laisser themedesignermode en prod (recompile à chaque page).
4. « Mon changement JS (amd/src/) n’a aucun effet. »
→ En prod, cachejs=true sert amd/build/ : rebuild avec grunt amd. En dev, mettez $CFG->cachejs = false; pour servir src/ directement. Ne jamais éditer amd/build/ (écrasé).
5. « Ma nouvelle capability / tâche / fonction WS n’existe pas. »
→ db/access.php, db/tasks.php, db/services.php ne sont relus qu’à l’upgrade. Bumpez version.php puis php admin/cli/upgrade.php, purgez.
🔴 Plugins
6. « L’update de mon activité écrit sur la mauvaise ligne. »
→ À l’_update_instance, l’id est dans $data->instance, pas $data->id. Ajoutez $data->id = $data->instance;.
7. « Mes items disparaissent du formulaire à la réédition. »
→ Données hors table principale non pré-remplies. Implémentez data_preprocessing() dans mod_form.
8. « Mon activité disparaît après duplication de cours. »
→ Pas de backup Moodle2 : déclarez FEATURE_BACKUP_MOODLE2 et fournissez backup/moodle2/.
9. « Mon bloc n’apparaît pas sur le tableau de bord. »
→ Capability :myaddinstance manquante (distincte de :addinstance).
10. « J’ai baissé/réutilisé un numéro de version et tout casse. »
→ La version ne doit jamais reculer (entier monotone). Incrémentez toujours.
🔴 Sécurité
11. « Un utilisateur accède aux données d’un autre. »
→ IDOR : la capability autorise « gérer des X », pas « ce X-ci est le sien ». Filtrez par userid dans chaque requête. Autorisation ≠ appartenance.
12. « Mon action mutante marche sans jeton. »
→ Il manque require_sesskey() (ou confirm_sesskey()) sur l’action. Toute mutation exige le jeton CSRF.
13. « Du HTML utilisateur s’affiche brut / faille XSS. »
→ Sortie non échappée. Utilisez s(), format_string() (texte), format_text() (HTML riche) à la sortie. En Mustache, {{ }} (jamais {{{ }}} pour de la donnée utilisateur).
🔴 Formulaires / DML
14. « Erreur SQL bizarre après un changement de schéma. »
→ En test PHPUnit/Behat, relancez init.php (la base de test n’a pas vos nouvelles tables). En dev normal, rejouez l’upgrade.
15. « Dépassement mémoire dans une tâche cron. »
→ get_records() charge tout en mémoire. Utilisez get_recordset() (streaming) + close().
16. « Requête N+1, page très lente. »
→ Une requête par élément dans une boucle. Faites une jointure ou get_records_list(). perfdebug révèle le nombre de requêtes.
🔴 JavaScript / Frontend
17. « Ajax.call renvoie un objet bizarre. »
→ Il renvoie un tableau de promesses : Ajax.call([...])[0] puis await.
18. « Mon écouteur meurt après un rafraîchissement AJAX. »
→ Nœud remplacé → écouteur perdu. Délégation d’événements sur un ancêtre stable + closest('[data-…]').
19. « Erreur top-level await dans mon module. »
→ Interdit en AMD. Mettez l’await dans une fonction async (ex. init).
20. « Ma modale AJAX ne s’ouvre pas en test Behat. »
→ Ajoutez le tag @javascript au scénario (sinon driver sans JS).
🔴 Web services / Next.js
21. « Mon appel WS renvoie 200 mais rien n’est fait. »
→ Moodle renvoie HTTP 200 même en erreur ; l’erreur est dans le corps (exception, errorcode). Inspectez le payload, pas le status.
22. « Mon token fuit dans le bundle client. »
→ Variable NEXT_PUBLIC_* = inlinée côté client. Le token va dans une variable serveur (sans NEXT_PUBLIC_), route handlers / server actions uniquement. Révoquez le token exposé.
23. « CORS bloque mon appel Moodle depuis le navigateur. » → N’appelez pas Moodle depuis le navigateur (token + CORS). Passez par votre backend Next.js (proxy).
24. « Token valide mais appel refusé (permission). »
→ Capability métier de la fonction manquante sur le compte de service. webservice/rest:use ne suffit pas.
🔴 LTI
25. « Launch LTI : invalid signature. » → Mauvais JWKS orienté (chacun signe avec sa clé privée, l’autre valide avec la clé publique correspondante). NTP/clock skew possible.
26. « L’outil LTI ne se charge pas en iframe. » → Cookies tiers (SameSite=None; Secure, ITP, CHIPS). Tester en « New window ».
FAQ franche
Moodle est-il headless ? Non par défaut, mais son API de web services (Partie 9) et LTI (Partie 10) permettent de le piloter et de l’intégrer. Pour une app Next.js : web services (token, REST) côté serveur.
Faut-il Composer pour développer un plugin ?
Pas pour le plugin lui-même (Moodle ne l’utilise pas pour les plugins). Composer sert à l’outillage de dev (PHPUnit, moodle-plugin-ci). Un plugin s’installe par git/zip, pas par Composer.
Y a-t-il du hot reload ?
Non façon Vite. En dev : $CFG->cachejs=false + $CFG->themedesignermode=true évitent les rebuilds, mais le cycle reste sauvegarde → reload. Pour du JS lourd (React embarqué), npm run watch (esbuild) régénère amd/build/.
Pourquoi PHP ? / Puis-je écrire un plugin en autre chose ? Le core est en PHP ; les plugins serveur sont en PHP (imposé). Le frontend est en JS (AMD/ES6, React possible en îlot, Partie 7). Pas de plugin serveur en Node/Python.
Puis-je modifier le code du core ? Non. « Core hack » = écrasé à chaque upgrade, coupe des correctifs de sécurité. Tout se fait en plugins (Parties 6–8) ou par contribution amont.
Quelle version de Moodle choisir pour un projet ? Dernière stable (5.2) par défaut ; dernière LTS si stabilité prioritaire. Vérifiez la compatibilité des plugins et de l’environnement (PHP 8.3+, BDD récentes). Ch. 1.5.
local, block ou mod ?
local_ = fonctionnalité transversale du site. block_ = boîte latérale/dashboard. mod_ = activité de cours (notée, dans une section). Ch. 6.1.
React ou pas React dans Moodle ?
La plupart du temps non : Mustache + core/* suffisent. React se justifie pour une UI riche à état client complexe (éditeur, wizard). Ch. 7.4/7.6.
Peut-on vraiment tout personnaliser dans un thème ? Oui (100 % du HTML/CSS/JS) — mais le coût est la maintenance des surcharges à travers les upgrades. Ch. 8.4.
Comment gagner de l’argent avec un plugin GPL ? Support, sur-mesure, hébergement, versions « pro » par abonnement au service — jamais licence propriétaire. Ch. 11.8.
La règle d’or, répétée
Ça ne marche pas ? → 1. Purgez les caches. 2. Avez-vous bumpé
version.php(sidb/*.phpchangé) ? 3. Débogage activé (DEBUG_DEVELOPER) — lisez l’erreur réelle. 4. Cherchez en anglais (glossaire ch. 12.1) sur moodledev.io, les forums, le tracker.
Chapitre suivant : 04 — Ressources.