Skip to Content
Interop EdTechPartie 13 — Cookbook & Annexes13.2 — Modèles prêts à l'emploi

Chapitre 13.2 — Modèles prêts à l’emploi

⏱️ TL;DR — Cinq squelettes copier-coller, commentés, prêts à adapter : un imsmanifest.xml SCORM 1.2 minimal et valide, un statement xAPI type, un cmi5.xml minimal, une requête de jeton LTI Advantage et un POST de 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 (namespace cmi5.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é, scoreMaximum absent).

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 endpoint reçu au lancement ne donne aucun droit : l’AU doit d’abord POSTer l’URL fetch pour obtenir son auth-token, puis lire LMS.LaunchData (launchMode, masteryScore, moveOn, returnURL) avant d’émettre initialized. 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/score

Le 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 %20 dans le corps), jamais par des virgules — une virgule provoque un invalid_scope. Demande le minimum de scopes (moindre privilège), et le scope renvoyé peut être plus restreint que le scope demandé.

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 = le sub de l’élève reçu au lancement (jamais l’e-mail).
  • activityProgress : Initialized / Started / InProgress / Submitted / Completed.
  • gradingProgress : NotReady / Failed / Pending / PendingManual / FullyGraded — vise FullyGraded pour une note définitive.
  • Pour corriger une note, on ne modifie pas le result (lecture seule) : on republie un Score avec un timestamp plus 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.xml cadre chaque nouveau module, et le couple « requête de jeton + POST de Score » vit dans le module services/ 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 du CourseStructure sur adlnet.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 sur 1edtech.org.


Chapitre suivant : 13.3 — Checklists — les listes à cocher pour ne rien oublier avant de livrer.

Last updated on