Files
bizzleandClaude Opus 4.8 4036ad5839 Sync Canvas LMS export feature + refreshed Settings screenshot
Brings the share build up to date with the main project: QTI .zip export
and optional direct Canvas API push, plus the Canvas settings card.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:43:35 -04:00

56 lines
2.9 KiB
JavaScript

// lib/canvas/manifest.js — the two wrapper files that make a folder of QTI XML into a
// Canvas-importable package: imsmanifest.xml (IMS Content Packaging) and the Canvas
// proprietary assessment_meta.xml (quiz-level settings).
import { xmlEsc } from "./qti";
// Canvas quiz-level settings. `title` is the only thing Canvas truly needs; everything
// else has a sensible Canvas default if omitted. We emit the fields the user can set.
export function renderAssessmentMeta(ir, assessmentIdent) {
const q = ir.quiz;
const lines = [];
const tag = (name, val) => lines.push(`<${name}>${xmlEsc(String(val))}</${name}>`);
const bool = (name, val) => tag(name, val ? "true" : "false");
const opt = (name, val) => { if (val !== "" && val != null) tag(name, val); };
tag("title", q.title);
tag("description", q.description || "");
tag("quiz_type", q.quizType);
tag("points_possible", q.pointsPossible);
opt("time_limit", q.timeLimit);
tag("allowed_attempts", q.allowedAttempts);
tag("scoring_policy", q.scoringPolicy);
bool("shuffle_answers", q.shuffleAnswers);
bool("show_correct_answers", q.showCorrectAnswers);
bool("one_question_at_a_time", q.oneQuestionAtATime);
bool("cant_go_back", q.cantGoBack);
opt("access_code", q.accessCode);
opt("due_at", q.dueAt);
opt("unlock_at", q.unlockAt);
opt("lock_at", q.lockAt);
return `<?xml version="1.0" encoding="UTF-8"?>
<quiz identifier="${xmlEsc(assessmentIdent)}" xmlns="http://canvas.instructure.com/xsd/cccv1p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://canvas.instructure.com/xsd/cccv1p0 https://canvas.instructure.com/xsd/cccv1p0.xsd">
${lines.join("\n")}
</quiz>`;
}
export function renderManifest(assessmentIdent, manifestIdent) {
const assessmentHref = `${assessmentIdent}/${assessmentIdent}.xml`;
const metaHref = `${assessmentIdent}/assessment_meta.xml`;
const depIdent = `${assessmentIdent}_dependency`;
return `<?xml version="1.0" encoding="UTF-8"?>
<manifest identifier="${xmlEsc(manifestIdent)}" xmlns="http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1" xmlns:lom="http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imscp_v1p2_v1p0.xsd">
<metadata><schema>IMS Content</schema><schemaversion>1.1.3</schemaversion></metadata>
<organizations/>
<resources>
<resource identifier="${xmlEsc(assessmentIdent)}" type="imsqti_xmlv1p2/imscc_xmlv1p1/assessment" href="${xmlEsc(assessmentHref)}">
<file href="${xmlEsc(assessmentHref)}"/>
<dependency identifierref="${xmlEsc(depIdent)}"/>
</resource>
<resource identifier="${xmlEsc(depIdent)}" type="associatedcontent/imscc_xmlv1p1/learning-application-resource" href="${xmlEsc(metaHref)}">
<file href="${xmlEsc(metaHref)}"/>
</resource>
</resources>
</manifest>`;
}