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 où 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/src → amd/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).