Chapitre 13.2 — Modèles prêts à l’emploi
⏱️ TL;DR — Cinq squelettes copier-coller, commentés, prêts à adapter : un
imsmanifest.xmlSCORM 1.2 minimal et valide, un statement xAPI type, uncmi5.xmlminimal, une requête de jeton LTI Advantage et unPOSTde Score AGS. Change les identifiants, les URLs et les valeurs — la structure, elle, est correcte. Chaque modèle renvoie au chapitre du cours qui l’explique en détail. Les valeurs figées (namespaces SCORM, IRIs de verbes, types de média) sont exactes ; celles à confirmer (namespacecmi5.xml) sont signalées en commentaire.
🎯 Objectifs
- Démarrer un paquet, un statement ou une intégration sans repartir de zéro.
- Repérer, dans chaque modèle, ce qui est figé et ce qui est à personnaliser.
- Éviter les erreurs de forme (namespace oublié, scope mal séparé,
scoreMaximumabsent).
Modèle 1 — imsmanifest.xml SCORM 1.2 minimal
Un module à un seul SCO. Les namespaces sont figés en SCORM 1.2 (ne les tape jamais de mémoire, copie-les). Personnalise les identifier, les <title> et les href. Détail complet en Partie 3.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Racine : identifiant unique du paquet + version LIBRE du contenu + namespaces FIGÉS -->
<manifest identifier="FORMACAMPUS_SECU_NUM" version="1.0"
xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2"
xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd
http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd">
<!-- 1. METADATA : quel standard, quelle version (FIGÉ : "ADL SCORM" + "1.2") -->
<metadata>
<schema>ADL SCORM</schema>
<schemaversion>1.2</schemaversion>
</metadata>
<!-- 2. ORGANIZATIONS : l'arbre de navigation. default = l'organisation active -->
<organizations default="ORG_SECU">
<organization identifier="ORG_SECU">
<title>Sécurité numérique</title>
<!-- item = entrée de menu ; identifierref pointe une <resource> ci-dessous -->
<item identifier="ITEM_INTRO" identifierref="RES_INTRO">
<title>Introduction à la sécurité</title>
</item>
</organization>
</organizations>
<!-- 3. RESOURCES : l'inventaire. scormType="sco" => lançable ET dialoguant -->
<resources>
<resource identifier="RES_INTRO" type="webcontent"
adlcp:scormType="sco" href="intro/index.html">
<file href="intro/index.html"/> <!-- la page de lancement DOIT aussi être listée -->
<file href="shared/scorm-api.js"/>
<file href="shared/styles.css"/>
</resource>
</resources>
</manifest>Points de contrôle avant de zipper : default pointe une organization existante, chaque identifierref correspond à un identifier de <resource>, la page de lancement (href) est aussi listée en <file>, le fichier est en UTF-8 sans BOM. Le .zip = le PIF, avec imsmanifest.xml à la racine.
Modèle 2 — Statement xAPI type
Un statement enrichi (score, réussite, contexte). Le noyau actor / verb / object suffit à être valide ; le reste s’ajoute quand on a quelque chose à dire. Réutilise un verbe ADL existant (ne l’invente pas). Détail en Partie 6.
{
"actor": {
"objectType": "Agent",
"name": "Léa Martin",
"mbox": "mailto:lea.martin@formacampus.fr"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/completed",
"display": { "fr-FR": "a terminé", "en-US": "completed" }
},
"object": {
"objectType": "Activity",
"id": "https://formacampus.fr/xapi/activities/atelier-soudure",
"definition": {
"name": { "fr-FR": "Atelier soudure d'angle" },
"description": { "fr-FR": "Atelier pratique en présentiel" },
"type": "http://adlnet.gov/expapi/activities/course"
}
},
"result": {
"score": { "scaled": 0.92, "raw": 46, "min": 0, "max": 50 },
"success": true,
"completion": true,
"duration": "PT1H15M"
},
"context": {
"registration": "3d6d3e0c-1f2b-4b8a-8c1a-9e2f7a6b5c4d",
"contextActivities": {
"parent": [{ "id": "https://formacampus.fr/xapi/activities/parcours-soudure" }]
}
},
"timestamp": "2026-03-14T10:45:00+01:00"
}L’envoyer au LRS (l’id/stored seront posés par le LRS) :
POST /xapi/statements HTTP/1.1
Host: lrs.formacampus.fr
Authorization: Basic bG9naW46bW90ZGVwYXNzZQ==
X-Experience-API-Version: 1.0.3
Content-Type: application/json
{ "...": "le statement ci-dessus" }⚠️ Piège — Oublier l’en-tête
X-Experience-API-Version: le LRS rejette l’appel. Il est obligatoire sur toutes les requêtes xAPI, en lecture comme en écriture.
Modèle 3 — cmi5.xml minimal
La structure de cours cmi5 : un <course> et un <au> (l’unité lançable). Les attributs moveOn et masteryScore décident de la satisfaction. Détail en Partie 8.
<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns du CourseStructure cmi5 : reprends la valeur EXACTE du XSD cmi5 courant sur adlnet.gov -->
<courseStructure xmlns="https://w3id.org/xapi/profiles/cmi5/v1/CourseStructure.xsd">
<!-- Le cours : un id (IRI unique), un titre, une description -->
<course id="https://formacampus.fr/cmi5/securite-numerique">
<title><langstring lang="fr-FR">Sécurité numérique</langstring></title>
<description><langstring lang="fr-FR">Parcours de sensibilisation à la cybersécurité</langstring></description>
</course>
<!-- Un AU (Assignable Unit) = l'unité LANÇABLE (l'équivalent moderne du SCO) -->
<!-- moveOn : Passed | Completed | CompletedAndPassed | CompletedOrPassed | NotApplicable -->
<!-- masteryScore : seuil de réussite, de 0 à 1 -->
<au id="https://formacampus.fr/cmi5/au/quiz"
moveOn="CompletedOrPassed"
masteryScore="0.8">
<title><langstring lang="fr-FR">Quiz de validation</langstring></title>
<description><langstring lang="fr-FR">Test final du module</langstring></description>
<!-- url : la page de lancement de l'AU, relative à la racine du paquet -->
<url>au/quiz/index.html</url>
</au>
<!-- Pour regrouper plusieurs AU, enveloppe-les dans un <block> :
<block id="https://formacampus.fr/cmi5/block/pratique">
<title><langstring lang="fr-FR">Mise en pratique</langstring></title>
<au ...>...</au>
<au ...>...</au>
</block>
-->
</courseStructure>⚠️ Piège — Le paramètre
endpointreçu au lancement ne donne aucun droit : l’AU doit d’abord POSTer l’URLfetchpour obtenir sonauth-token, puis lireLMS.LaunchData(launchMode,masteryScore,moveOn,returnURL) avant d’émettreinitialized. L’ordre est non négociable (Partie 8).
Modèle 4 — Requête de jeton LTI Advantage
Pour appeler un service (AGS, NRPS), ton outil échange un JWT client_assertion signé avec sa clé privée contre un access_token OAuth2. Détail en Partie 10.
Le corps de la requête (grant_type=client_credentials) :
POST /mod/lti/token.php HTTP/1.1
Host: moodle.ecole.fr
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6InN0dWRpby0yMDI2In0.eyJpc3M...
&scope=https://purl.imsglobal.org/spec/lti-ags/scope/scoreLe client_assertion, décodé, est un JWT court signé par ta clé (header : alg = RS256, kid = ta clé) :
{
"iss": "formacampus-studio-client-id",
"sub": "formacampus-studio-client-id",
"aud": "https://moodle.ecole.fr/mod/lti/token.php",
"iat": 1893455940,
"exp": 1893456240,
"jti": "req-a1b2c3-unique"
}Réponse — un access_token de type Bearer, à mettre en cache jusqu’à expires_in :
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "https://purl.imsglobal.org/spec/lti-ags/scope/score"
}💡 Réflexe — Plusieurs scopes se séparent par des espaces (encodés
%20dans le corps), jamais par des virgules — une virgule provoque uninvalid_scope. Demande le minimum de scopes (moindre privilège), et lescoperenvoyé peut être plus restreint que lescopedemandé.
Modèle 5 — POST de Score AGS
Écrire une note dans le carnet du LMS : POST sur {lineitem}/scores avec le bearer obtenu au Modèle 4. scoreGiven et scoreMaximum vont toujours ensemble. Détail en Partie 10.
POST /mod/lti/services.php/2/lineitems/5/lineitem/scores HTTP/1.1
Host: moodle.ecole.fr
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...
Content-Type: application/vnd.ims.lis.v1.score+json
{
"userId": "u-2342",
"scoreGiven": 17,
"scoreMaximum": 20,
"activityProgress": "Completed",
"gradingProgress": "FullyGraded",
"timestamp": "2026-05-12T14:32:05.000Z",
"comment": "Bravo. Revois la question 7."
}userId= lesubde l’élève reçu au lancement (jamais l’e-mail).activityProgress:Initialized/Started/InProgress/Submitted/Completed.gradingProgress:NotReady/Failed/Pending/PendingManual/FullyGraded— viseFullyGradedpour une note définitive.- Pour corriger une note, on ne modifie pas le
result(lecture seule) : on republie un Score avec untimestampplus récent.
🧭 Sur FormaCampus — Ces cinq modèles sont les fichiers de départ du studio : le manifeste sert de base à chaque export SCORM, le statement type alimente l’appli mobile de tracking, le
cmi5.xmlcadre chaque nouveau module, et le couple « requête de jeton +POSTde Score » vit dans le moduleservices/qui parle aux Moodle des académies. On copie, on renomme, on livre.
📚 La spec — Manifeste SCORM 1.2 : livre CAM (ADL). Statement xAPI et API du LRS : xAPI (ADL).
cmi5.xml: cmi5 Specification (ADL) — confirme le namespace duCourseStructuresuradlnet.gov. Requête de jeton et Score : LTI 1.3 Security Framework et Assignment and Grade Services (1EdTech). Les types de média (application/vnd.ims.lis.v1.score+json…) sont normés sur1edtech.org.
Chapitre suivant : 13.3 — Checklists — les listes à cocher pour ne rien oublier avant de livrer.