Skip to Content
MoodlePartie 14 — Mini-jeux (exploration guidée)14 — Blocs & frontend (JS/Mustache)

Terrain 14 — Blocs & frontend

⏱️ TL;DR — 25 mini-jeux ⭐⭐⭐ pour construire un bloc (block_progressinfo) et maîtriser le frontend Moodle : Output API, templates Mustache, modules JavaScript AMD, passage PHP→JS, composants du cœur. Où ton expérience React rencontre les conventions Moodle.

💡 Pour un dev React — Moodle n’utilise pas React nativement (il a son propre système : Mustache + AMD + un peu de core JS). Mais la logique (composants, données→vue, modules) t’est familière. Certains thèmes/plugins intègrent React (Partie 7).


🕹️ Mini-jeu 1 — La structure d’un bloc ⭐⭐⭐ 💻

But : l’anatomie. Mission : crée blocks/progressinfo/block_progressinfo.php.

✅ Solution

Un bloc = dossier blocks/progressinfo/ + classe block_progressinfo (dans le fichier du même nom) + version.php + lang/. Frankenstyle block_progressinfo. Type de plugin le plus simple après local — idéal deuxième plugin.


🕹️ Mini-jeu 2 — version.php du bloc ⭐⭐⭐ 💻

But : l’identité. Mission : crée version.php (component = 'block_progressinfo').

✅ Solution

Même structure qu’au terrain 13 : component, version, requires. Le préfixe block_ indique le type. Installé via Notifications comme tout plugin.


🕹️ Mini-jeu 3 — La classe minimale ⭐⭐⭐ 💻

But : le squelette du bloc. Mission : définis la classe qui étend block_base avec init(). Étapes :

<?php class block_progressinfo extends block_base { public function init() { $this->title = get_string('pluginname', 'block_progressinfo'); } }

✅ Solution

init() (obligatoire) fixe le titre. La classe hérite de block_base (tout le comportement de bloc). Sans get_content() (jeu 4), le bloc est vide. Le titre vient du fichier de langue.


🕹️ Mini-jeu 4 — get_content() ⭐⭐⭐⭐ 💻

But : le contenu du bloc. Mission : implémente get_content(). Étapes :

public function get_content() { if ($this->content !== null) { return $this->content; } $this->content = new stdClass(); $this->content->text = 'Votre progression : 42 %'; $this->content->footer = ''; return $this->content; }

✅ Solution

get_content() renvoie un objet avec text (et footer). Le cache interne (if ($this->content !== null)) évite de recalculer. C’est le cœur d’un bloc. On remplacera le texte en dur par un vrai calcul + template (jeux 10–11).


🕹️ Mini-jeu 5 — applicable_formats() ⭐⭐⭐ 💻

But : où le bloc peut aller. Mission : limite le bloc aux pages de cours. Étapes :

public function applicable_formats() { return ['course-view' => true, 'my' => true]; }

✅ Solution

applicable_formats() déclare le bloc est ajoutable (cours, tableau de bord my, site…). Évite d’offrir un bloc « progression de cours » sur des pages sans cours. Contrôle contextuel du bloc.


🕹️ Mini-jeu 6 — Les capabilities du bloc ⭐⭐⭐⭐ 💻

But : qui peut l’ajouter. Mission : déclare addinstance et myaddinstance dans db/access.php.

✅ Solution

Un bloc déclare block/progressinfo:addinstance (ajouter dans un cours) et myaddinstance (au tableau de bord) — terrain 11, jeu 12. Elles décident qui peut poser le bloc. Standard pour tout bloc.


🕹️ Mini-jeu 7 — Installer et ajouter le bloc ⭐⭐ 💻

But : le voir en action. Mission : installe, puis en mode édition d’un cours, ajoute « Progress info ».

✅ Solution

Après install (Notifications) + purge des caches, le bloc apparaît dans « Ajouter un bloc » (tiroir droit, mode édition). Tu le poses dans un cours. Multi-instances possibles.


🕹️ Mini-jeu 8 — Configuration par instance ⭐⭐⭐⭐ 💻

But : régler chaque bloc. Mission : crée edit_form.php pour un champ de config (ex. objectif %). Étapes :

<?php class block_progressinfo_edit_form extends block_edit_form { protected function specific_definition($mform) { $mform->addElement('text', 'config_goal', 'Objectif (%)'); $mform->setType('config_goal', PARAM_INT); } }

✅ Solution

edit_form.php ajoute des réglages par instance (chaque bloc posé a sa config). Les champs config_* sont accessibles via $this->config->goal dans get_content(). Active aussi has_config()/instance_allow_config selon besoin.


🕹️ Mini-jeu 9 — Ajouter au tableau de bord ⭐⭐ 💻

But : un bloc perso. Mission : ajoute le bloc à ton Tableau de bord (my).

✅ Solution

Grâce à 'my' => true (jeu 5) + myaddinstance (jeu 6), le bloc est ajoutable au Tableau de bord. Les blocs « perso » (progression, tâches) y trouvent leur place. L’admin peut l’imposer par défaut (terrain 10, jeu 17).


🕹️ Mini-jeu 10 — Un template Mustache ⭐⭐⭐ 💻

But : sortir le HTML du PHP. Mission : crée templates/content.mustache. Étapes :

<div class="progressinfo"> <div class="bar" style="width: {{percent}}%"></div> <span>{{percent}} %</span> </div>

✅ Solution

Comme au terrain 13, on rend le HTML via Mustache (auto-échappement, thémable). get_content() appellera render_from_template('block_progressinfo/content', $data). Sépare logique et présentation.


🕹️ Mini-jeu 11 — Rendre le template dans le bloc ⭐⭐⭐⭐ 💻

But : brancher template et bloc. Mission : utilise le renderer dans get_content(). Étapes :

global $OUTPUT; $data = ['percent' => 42]; $this->content->text = $OUTPUT->render_from_template('block_progressinfo/content', $data);

✅ Solution

$OUTPUT->render_from_template($name, $data) produit le HTML depuis Mustache + données. On remplace le texte en dur (jeu 4) par ce rendu propre. Pour un renderer dédié, on crée classes/output/ (terrain 13, jeu 17).


🕹️ Mini-jeu 12 — Un module JS AMD ⭐⭐⭐⭐ 💻

But : du JavaScript propre. Mission : crée amd/src/toggle.js. Étapes :

export const init = () => { document.querySelectorAll('.progressinfo').forEach((el) => { el.addEventListener('click', () => el.classList.toggle('expanded')); }); };

✅ Solution

Moodle utilise des modules AMD (ES6 transpilé) dans amd/src/. Chaque module exporte des fonctions (souvent init). Système modulaire natif de Moodle (pas de <script> en dur). Ton terrain JS, avec les conventions Moodle.


🕹️ Mini-jeu 13 — Builder l’AMD ⭐⭐⭐⭐ 💻

But : compiler pour la prod. Mission : build amd/srcamd/build avec Grunt. Étapes :

npx grunt amd --root=blocks/progressinfo

✅ Solution

amd/src/ (source) est transpilé/minifié en amd/build/ (servi). L’outil Grunt de Moodle fait le build. En dev, $CFG->cachejs = false (terrain 12) sert la source directement. On commite les deux dossiers pour la distribution.


🕹️ Mini-jeu 14 — Charger le JS depuis PHP ⭐⭐⭐⭐ 💻

But : activer le module. Mission : appelle le module AMD depuis get_content(). Étapes :

$this->page->requires->js_call_amd('block_progressinfo/toggle', 'init');

✅ Solution

js_call_amd($module, $function, $args) charge le module AMD et appelle sa fonction (ici init) — la façon officielle d’exécuter du JS. Gère dépendances et chargement différé. Le pont PHP → JS.


🕹️ Mini-jeu 15 — Passer des données PHP → JS ⭐⭐⭐⭐ 💻

But : paramétrer le JS. Mission : passe le pourcentage au module JS. Étapes :

$this->page->requires->js_call_amd('block_progressinfo/toggle', 'init', [$percent]);

✅ Solution

Le 3e argument (tableau) est passé en arguments à la fonction JS (sérialisé en JSON). export const init = (percent) => {...}. Façon propre d’injecter des données serveur dans le JS, sans variable globale.


🕹️ Mini-jeu 16 — Les chaînes côté JS ⭐⭐⭐⭐ 💻

But : i18n en JavaScript. Mission : récupère une chaîne traduite dans le JS. Étapes :

import {get_string as getString} from 'core/str'; const label = await getString('addtask', 'block_progressinfo');

✅ Solution

Le module core/str charge les chaînes de langue côté JS (asynchrone) — pas de texte en dur, i18n respectée jusque dans le JavaScript. Cohérent avec get_string() PHP. Réflexe pour du JS localisé.


🕹️ Mini-jeu 17 — Utiliser un composant du cœur ⭐⭐⭐⭐ 💻

But : réutiliser l’UI Moodle. Mission : affiche une notification JS via core/notification. Étapes :

import Notification from 'core/notification'; Notification.addNotification({message: 'Fait !', type: 'success'});

✅ Solution

Le cœur expose des modules JS réutilisables : core/notification, core/modal, core/ajax, core/templates (rendre du Mustache côté JS !). Réutilise-les au lieu de réinventer. Cohérence UI garantie.


🕹️ Mini-jeu 18 — Rendre du Mustache côté JS ⭐⭐⭐⭐⭐ 💻

But : UI dynamique. Mission : rends un template depuis le JS avec core/templates. Étapes :

import Templates from 'core/templates'; const {html, js} = await Templates.renderForPromise('block_progressinfo/content', {percent: 80}); Templates.replaceNodeContents(target, html, js);

✅ Solution

core/templates rend les mêmes templates Mustache côté client → UI dynamique sans dupliquer le markup (PHP et JS partagent le template). Proche du rendu React côté client. Puissant pour de l’interactif.


🕹️ Mini-jeu 19 — Les icônes (pix API) ⭐⭐⭐ 💻

But : des icônes cohérentes. Mission : affiche une icône via $OUTPUT->pix_icon. Étapes :

echo $OUTPUT->pix_icon('t/check', get_string('done', 'block_progressinfo'));

✅ Solution

La pix API sert les icônes du thème (cohérentes, thémables, accessibles avec alt). Tes propres icônes vont dans pix/ du plugin. Évite les <img> en dur. Détail de finition et d’accessibilité.


🕹️ Mini-jeu 20 — Accessibilité du bloc ⭐⭐⭐⭐ 💻

But : utilisable par tous. Mission : ajoute ARIA/alt et vérifie la navigation clavier.

✅ Solution

Un bloc doit être accessible : rôles/labels ARIA, contraste, focus clavier, alt sur les icônes. Moodle vise WCAG (obligation en éducation, terrain 01, jeu 27). L’accessibilité fait partie de la qualité (terrain 16).


🕹️ Mini-jeu 21 — Tailwind / CSS custom ⭐⭐⭐ 💻

But : styliser. Mission : ajoute styles.css au bloc et une classe stylée.

✅ Solution

Un plugin peut fournir styles.css (chargé automatiquement). Le style « global » se fait plutôt dans le thème (SCSS, terrain 10, jeu 6). Pour du Tailwind, on l’intègre au build d’un thème custom (Partie 7/8). Purge le cache thème après.


🕹️ Mini-jeu 22 — Calculer une vraie progression ⭐⭐⭐⭐ 💻

But : du contenu réel. Mission : remplace le 42 % en dur par la vraie complétion du cours (Completion API).

✅ Solution

On interroge la Completion API (completion_info) pour le % réel d’achèvement de l’utilisateur dans le cours (terrain 08). Le bloc devient utile. Toujours vérifier la capability/contexte avant d’afficher des données.


🕹️ Mini-jeu 23 — Débuguer le frontend ⭐⭐⭐ 💻

But : JS + cache. Mission : modifie le JS et vois-le sans rebuild (mode dev).

✅ Solution

Avec $CFG->cachejs = false (terrain 12), Moodle sert amd/src/ directement → modifications visibles au rechargement. En prod, il faut rebuilder (Grunt) et purger. Confusion fréquente : « mon JS ne change pas » = cache JS actif.


🕹️ Mini-jeu 24 — Le coding style (JS + PHP) ⭐⭐⭐⭐ 💻

But : conformité. Mission : lance moodle-cs (PHP) et le lint JS (ESLint via Grunt).

✅ Solution

Moodle vérifie PHP (moodle-cs) et JS (ESLint intégré à Grunt : grunt eslint). Vise le vert des deux avant distribution (Partie 11). L’hygiène JS que tu connais déjà, avec les règles Moodle.


🕹️ Mini-jeu 25 — Récap bloc & frontend ⭐⭐⭐ 💻

But : capstone du terrain. Mission : block_progressinfo complet : config par instance, vraie progression (Completion API), template Mustache, module AMD interactif (avec i18n JS), icônes pix, accessible, coding style vert.

✅ Solution

Tu maîtrises le bloc et le frontend Moodle (Output API, Mustache, AMD, core JS) — c’est block_progressinfo du cours (Partie 6.4). Reste le type le plus riche : le module d’activité (terrain 15).


Terrain suivant : 15 — Module d’activité (mod).