Chapitre 9.3 — Les fonctions externes du core
Partie 9 : Web Services — Chapitre 3/5 Public : dev Next.js/React. On a l’architecture (9.1) et un token sécurisé (9.2) ; on appelle maintenant les fonctions livrées par Moodle. Version de référence : Moodle 5.2.
⏱️ TL;DR
- Moodle livre des milliers de fonctions externes couvrant l’essentiel : cours, inscriptions, utilisateurs, notes, contenus, calendrier, messages…
- Le nommage est frankenstyle :
core_<domaine>_<action>(core_course_get_courses) ou<plugin>_<action>(mod_forum_get_forums_by_courses,gradereport_user_get_grade_items).- La doc API intégrée (Web services → Documentation de l’API, par service) donne la signature exacte de chaque fonction : paramètres (types, obligatoires), et structure de retour. C’est votre contrat.
- Appel REST :
POST /webservice/rest/server.phpavecwstoken,wsfunction,moodlewsrestformat=json, et les args. Les paramètres imbriqués suivent une notation spéciale (courseids[0]=2).- Fonctions phares :
core_webservice_get_site_info(test/handshake),core_course_get_courses,core_enrol_get_users_courses,enrol_manual_enrol_users,core_user_get_users,gradereport_user_get_grade_items,core_course_get_contents.- Toujours vérifier la présence d’un
exceptiondans le corps (HTTP 200 trompeur, ch. 9.1).- Une fonction ne renvoie/agit que dans la limite des capabilities du token (ch. 9.2).
🎯 Objectifs
- Trouver les fonctions du core et lire leur signature dans la doc intégrée.
- Comprendre le nommage et le format de réponse.
- Appeler en REST (curl + TypeScript), y compris avec des paramètres imbriqués.
- Réaliser les opérations courantes : lister cours, inscrire, lire notes, contenu de cours.
1. Où trouver les fonctions (et leur signature)
Ne devinez jamais une signature : Moodle documente chaque fonction, pour votre instance et votre version.
- Documentation de l’API intégrée : Administration → Serveur → Web services → Documentation de l’API (ou par service). Pour chaque fonction du service, elle liste : le nom, la description, les paramètres (nom, type, obligatoire/optionnel, défaut) et la structure de retour (arborescence typée). C’est la source d’autorité — plus fiable que n’importe quel tutoriel, car elle reflète votre version.
- L’API tester (Web services → API tester) : formulaire pour exécuter une fonction avec votre token et voir la réponse réelle. Idéal pour explorer avant de coder.
- La référence en ligne : moodledev.io/docs/apis/subsystems/external/functions et le code (
classes/external/des composants).
⚠️ Piège : les signatures évoluent entre versions (paramètres ajoutés/dépréciés). Fiez-vous à la doc API de votre instance 5.2, pas à un exemple trouvé pour une 4.x. L’API tester confirme le comportement réel.
2. Le nommage frankenstyle
Le nom d’une fonction encode son composant et son action :
| Fonction | Composant | Action |
|---|---|---|
core_course_get_courses | core_course | lister des cours |
core_enrol_get_users_courses | core_enrol | cours d’un utilisateur |
enrol_manual_enrol_users | enrol_manual | inscrire (méthode manuelle) |
core_user_get_users | core_user | rechercher des utilisateurs |
gradereport_user_get_grade_items | gradereport_user | notes d’un utilisateur |
mod_forum_get_forums_by_courses | mod_forum | forums d’un cours |
core_course_get_contents | core_course | contenu (sections/activités) d’un cours |
Convention : get_ = lecture, create_/update_/delete_/enrol_ = écriture. Les fonctions d’écriture exigent des capabilities plus fortes (ch. 9.2).
3. Anatomie d’un appel REST
L’endpoint : POST https://moodle.example.com/webservice/rest/server.php. Paramètres communs :
| Paramètre | Valeur |
|---|---|
wstoken | votre token (ch. 9.2) |
wsfunction | le nom de la fonction |
moodlewsrestformat | json |
| args | selon la signature |
3.1 Le handshake : core_webservice_get_site_info
La première fonction à appeler pour valider token + connexion : elle renvoie les infos du site et les fonctions autorisées pour ce token.
curl 'https://moodle.example.com/webservice/rest/server.php' \
--data-urlencode 'wstoken=VOTRE_TOKEN' \
--data-urlencode 'wsfunction=core_webservice_get_site_info' \
--data-urlencode 'moodlewsrestformat=json'Réponse (extrait) :
{
"sitename": "MaBoîte LMS",
"username": "svc_nextjs",
"userid": 42,
"release": "5.2",
"functions": [
{"name": "core_course_get_courses", "version": "2026042000"},
{"name": "core_enrol_get_users_courses", "version": "2026042000"}
]
}Le tableau functions liste exactement ce que ce token peut appeler (le service, ch. 9.1). Excellent point de départ de tout debug (« ma fonction n’est pas dans le service »).
3.2 Les paramètres imbriqués : la notation à connaître
Beaucoup de fonctions prennent des tableaux ou des structures. En REST (form-encoded), Moodle utilise une notation à crochets :
# core_course_get_courses avec options[ids][0]=2, options[ids][1]=5
curl 'https://moodle.example.com/webservice/rest/server.php' \
--data-urlencode 'wstoken=VOTRE_TOKEN' \
--data-urlencode 'wsfunction=core_course_get_courses' \
--data-urlencode 'moodlewsrestformat=json' \
--data-urlencode 'options[ids][0]=2' \
--data-urlencode 'options[ids][1]=5'Pour enrol_manual_enrol_users, chaque inscription est un objet dans un tableau enrolments :
--data-urlencode 'enrolments[0][roleid]=5' \
--data-urlencode 'enrolments[0][userid]=123' \
--data-urlencode 'enrolments[0][courseid]=2'⚠️ Piège n°1 des paramètres : la notation
enrolments[0][userid]=123doit être exacte (indices numériques, crochets imbriqués). Une erreur de structure donne une erreur de validationinvalidparameteravant l’exécution. La doc API montre la structure attendue ; l’API tester génère les bons noms de champs — copiez-les.
4. Les opérations courantes (curl + TypeScript)
Passons à TypeScript, tel qu’appelé côté serveur Next.js (ch. 9.5 détaille le client). On suppose un helper callMoodle(wsfunction, args) qui POST en form-encoded — construit au chapitre 5. Ici, on se concentre sur quelles fonctions et quels paramètres.
4.1 Lister les cours d’un utilisateur
// core_enrol_get_users_courses : les cours où userid est inscrit
const courses = await callMoodle('core_enrol_get_users_courses', {
userid: 123,
});
// → [{ id, shortname, fullname, progress, ... }, ...]4.2 Lister/rechercher des cours
// Tous les cours (ou par ids)
const all = await callMoodle('core_course_get_courses', {});
const some = await callMoodle('core_course_get_courses', {
'options[ids][0]': 2,
'options[ids][1]': 5,
});4.3 Inscrire un utilisateur (écriture)
// enrol_manual_enrol_users : inscrit userid dans courseid avec roleid
await callMoodle('enrol_manual_enrol_users', {
'enrolments[0][roleid]': 5, // 5 = étudiant (par défaut)
'enrolments[0][userid]': 123,
'enrolments[0][courseid]': 2,
});
// → null en cas de succès (pas de retour) ; exception sinonNécessite que le token porte enrol/manual:enrol sur le cours (ch. 9.2), et que la méthode d’inscription manuelle soit activée sur le cours.
4.4 Rechercher des utilisateurs
// core_user_get_users : recherche par critères
const users = await callMoodle('core_user_get_users', {
'criteria[0][key]': 'email',
'criteria[0][value]': 'alice@example.com',
});
// → { users: [{ id, username, fullname, email, ... }], warnings: [] }4.5 Lire les notes d’un utilisateur dans un cours
// gradereport_user_get_grade_items : les notes détaillées
const report = await callMoodle('gradereport_user_get_grade_items', {
courseid: 2,
userid: 123,
});
// → { usergrades: [{ courseid, userid, gradeitems: [{ itemname, gradeformatted, ... }] }] }4.6 Récupérer le contenu (structure) d’un cours
// core_course_get_contents : sections + modules du cours
const contents = await callMoodle('core_course_get_contents', {
courseid: 2,
});
// → [{ id, name, modules: [{ id, name, modname, url, ... }] }, ...]5. Le format des réponses (et les warnings)
Deux régularités à connaître :
- Structures typées : chaque fonction renvoie une arborescence stable (objets/tableaux) décrite par sa doc. Les champs de texte sont déjà formatés côté serveur (
external_format_string/external_format_text, ch. 9.4) — sûrs à afficher. - Les
warnings: beaucoup de fonctions renvoient un tableauwarningsà côté des données. Un warning n’est pas une exception : c’est un avertissement par élément (ex. « l’utilisateur X n’a pas pu être traité »). Inspectezwarningsmême en cas de succès global — sinon vous ratez des échecs partiels.
{
"users": [ { "id": 123, "fullname": "Alice" } ],
"warnings": [
{ "item": "user", "itemid": 999, "warningcode": "usernotfound", "message": "..." }
]
}⚠️ Piège : ignorer
warningssur une opération de masse (inscrire 500 utilisateurs) fait croire à un succès total alors que certains ont échoué. Traitezwarningscomme des erreurs partielles à remonter/logger.
6. Débogage : la checklist
Quand un appel échoue :
- HTTP 200 mais
exceptiondans le corps ? → lireerrorcode/message(ch. 9.1). accessexception/ fonction inconnue ? → la fonction n’est pas dans le service (vérifier viaget_site_info.functions, ch. §3.1) ou le protocole REST n’est pas activé.required_capability/nopermissions? → capability métier manquante sur le compte de service (ch. 9.2 §3).invalidparameter? → structure des paramètres incorrecte (notation à crochets, §3.2) — recopier depuis l’API tester.invalidtoken? → token faux/expiré/révoqué, ou IP non autorisée (ch. 9.2).
💡 Pour un dev React : votre réflexe « lire le status HTTP » ne suffit pas ici. Écrivez une fois un wrapper (
callMoodle) qui : parse le JSON, détecteexception, et jette une erreur typée — puis vous codez comme avec une API normale. Ce wrapper est le sujet du chapitre 5.
7. Synthèse
Le core expose une API vaste et auto-documentée. La méthode fiable : explorer la doc API + l’API tester de votre instance → repérer la fonction et sa signature exacte → l’appeler en REST avec la bonne notation de paramètres → vérifier l’exception et les warnings → afficher. Tout appel s’exécute dans la limite des capabilities du token. Vous savez maintenant consommer le core ; le chapitre suivant vous apprend à exposer vos propres fonctions quand le core ne suffit pas.
✏️ Exercices
Exercice 1 — Trouver la signature.
Vous voulez inscrire un utilisateur. Où trouvez-vous la structure exacte des paramètres de enrol_manual_enrol_users, et pourquoi pas un blog ?
✅ Solution
Dans la Documentation de l’API intégrée de votre instance (par service), qui liste les paramètres (enrolments = tableau d’objets {roleid, userid, courseid, ...}) pour votre version 5.2, et via l’API tester qui génère les noms de champs exacts (enrolments[0][userid]). Pas un blog, car les signatures évoluent entre versions : un exemple 4.x peut être obsolète. La doc de l’instance est la source d’autorité.
Exercice 2 — Handshake. Quelle fonction appelez-vous en premier pour valider votre intégration, et qu’apprenez-vous de sa réponse ?
✅ Solution
core_webservice_get_site_info : elle valide que le token et la connexion fonctionnent, et renvoie sitename, username/userid (l’identité du token), release (version), et surtout le tableau functions = la liste exacte des fonctions que ce token peut appeler (le service). C’est le point de départ de tout debug (« ma fonction est-elle bien dans le service ? »).
Exercice 3 — Paramètres imbriqués. Traduisez en notation REST : « lister les cours d’ids 3 et 7 ». Puis dites ce qui se passe si la notation est mal formée.
✅ Solution
core_course_get_courses avec options[ids][0]=3 et options[ids][1]=7 (form-encoded, indices numériques). Si la notation est mal formée (ex. options[ids]=3,7 ou mauvais crochets), Moodle renvoie une erreur invalidparameter à la validation (execute_parameters), avant d’exécuter la fonction. Copier la structure depuis l’API tester évite ces erreurs.
Exercice 4 — Échec partiel. Vous inscrivez 500 utilisateurs ; l’appel renvoie un succès HTTP 200. Que devez-vous vérifier avant de conclure au succès total ?
✅ Solution
Le tableau warnings de la réponse : il liste les échecs partiels (utilisateurs non trouvés, cours invalides…) sans lever d’exception globale. Un HTTP 200 et l’absence d’exception signifient seulement que l’appel a été traité, pas que chaque inscription a réussi. Il faut parcourir warnings, les logger/remonter, et réconcilier. Ignorer warnings masque des échecs silencieux.
Exercice 5 — Diagnostic.
Un appel renvoie {"exception":"webservice_access_exception","errorcode":"accessexception"}. Deux causes possibles ?
✅ Solution
(1) La fonction n’est pas dans le service attaché au token (vérifier via get_site_info.functions, l’ajouter au service côté admin). (2) Le protocole REST n’est pas activé ou le service est désactivé/l’utilisateur non autorisé. (Une capability métier manquante donnerait plutôt nopermissions/required_capability.) Checklist du §6 : commencer par le handshake pour isoler.
🧠 Quiz de révision
Q1. Où trouve-t-on la signature exacte d’une fonction et pourquoi cette source ?
Réponse
Q2. Que signifie le nommage core_enrol_get_users_courses ?
Réponse
core_enrol + action get_users_courses (lecture des cours d’un utilisateur). get_ = lecture ; create_/update_/delete_/enrol_ = écriture (capabilities plus fortes).Q3. Comment passe-t-on un paramètre tableau/objet en REST ?
Réponse
options[ids][0]=3, enrolments[0][userid]=123. La structure doit être exacte (indices numériques, imbrication) ; sinon erreur invalidparameter. L’API tester génère les bons noms de champs.Q4. Pourquoi faut-il inspecter warnings même en cas de succès ?
Réponse
warnings signale des échecs partiels (par élément) sans lever d’exception globale. Un HTTP 200 sans exception ne garantit pas que chaque élément d’une opération de masse a réussi ; ignorer warnings masque des échecs silencieux.Q5. Quelle fonction sert de handshake et qu’apprend-on de sa réponse ?
Réponse
core_webservice_get_site_info : elle valide token/connexion et renvoie l’identité du token, la version, et la liste des fonctions autorisées (functions) — la référence pour vérifier ce que le token peut appeler.Chapitre suivant : 04 — Créer sa propre fonction externe — exposer local_todolist en web service : la classe \core_external\external_api, le triptyque execute_parameters() / execute() / execute_returns(), la déclaration dans db/services.php, la validation typée, les capabilities, et la sortie sûre.