Repository navigation
Releases: falkenslab/moodle-driver
Release list
v0.8.0
New
- Back up part of a course (#57).
backupCourse(courseId, destDir, { sections, activities })backs up only the given sections (with everything in them) and activities or resources, without people's data or the course's own parts (groups, blocks, badges, events…); the driver reads the file back and refuses it if it holds more than asked. - Restore it at a chosen place (#57).
restoreCourseinto an existing course returns the newsectionIdsandactivityIds, and takessectionId(every restored activity into that section) orafterSectionId(the backup's sections as new sections after that one). Before the wizard, a backup from a newer Moodle, or with activity types the site doesn't offer, is refused instead of being restored partly and silently. - Know what a backup holds (#58).
backupContents(file)reads a.mbzlocally, with no browser or session: type, Moodle version, origin, sections, activities, plugins needed, whether it has users' data, each activity's dependencies outside the backup (restrictions, links, grade category), and what in it still identifies a person or a site (identifies). Restoring into an existing course also returnswarningsnaming the restrictions and links left broken. - Anonymous backups (#59).
anonymizeBackup(file, destFile)writes a copy of a backup that names no person (user ids, authors, creators, modifiers…) and no site or course of origin, leaving content, settings and files as they are. A backup of part of a course is anonymous by default (anonymous: falsekeeps the metadata). See the backup page for what a neutral origin changes in a restore. - The
moodlecommand gainsbackup-contentsandanonymize-backup, which need no site or profile. - Several sites in one browser: documented in the browser concepts page (one profile can hold sessions for several sites; two accounts on one site need separate profiles).
Fixes
startQuizAttemptstarts a quiz with a time limit (#56): it confirms Moodle's preflight dialog instead of waiting for a navigation that never comes, and also handles quizzes that open the attempt in a secure pop-up window. A quiz with a password is refused with a clearQuizError, and Moodle's own refusal message now reaches the error.
Internal
BackupErrormoved to the shared errors module; it's still exported the same way.- The
.mbzreading and writing uses Node's zlib with a small tar reader and writer: no new dependency.
v0.7.0
New
- Long lists a page at a time (#55).
notifications,conversations,conversation,forumDiscussions,discussion,assignmentSubmissions,quizResults,questions,participantsandcourseLogstakelimitandoffset. Withlimit, the list comes as{ items, more }(ListPage); without it, the same plain list as before, and TypeScript gives the right type for each call. The driver asks Moodle only for the page wherever Moodle can page (services' limits, a page's own paging); the docs say where it can't. Themoodlecommand takes--limitand--offsetand says when there are more. See Long lists. notifications({ unreadOnly: true, limit })pages over unread notifications.assignmentSubmissions,graderReportandcompletionReporttakeuserIds, to read only some participants.- New types:
ListPage,PageOptions,NotificationOptions,SubmissionOptions,ParticipantOptions,CourseLogOptions,QuizResultsOptionsand the results of the functions above.
Fixes
assignmentSubmissionsno longer creates empty submissions in Moodle just by reading (#54). Moodle's grading panel creates one for a participant who has none, so the panel is now opened only for participants with a submission; those without one come back without feedback files, workflow state or markers.rubric,allocateMarkerandsetWorkflowStateread the panel of a participant who submitted, where there is one. What reading still writes (a "not graded" grade for a submitted, ungraded participant, as Moodle's own grader does) is said in the docs.
Breaking changes
courseLogs(courseId, { limit })now returns{ items, more }instead of a list, like every paged function. Read.items;moresays whether to ask again withoffset. Called withoutlimit, it still returns a list (its default of 100 entries stays).
The Moodle sandbox regression suite passed for the touched areas (messages, forums, assignments, lifecycle).
v0.6.0
New
- Images and files embedded in rich text (#52): an
embedoption (local paths) oncreateActivity,updateActivity,addSection,editSection,startDiscussion,replyToPost,editPostandupdateCourse. The HTML names each file by its name (<img src="shot.png">); the driver uploads it where Moodle's editor would and rewrites the reference, so the image shows to everyone. See Embedding files in rich text. activityContent(pages, labels, books) and forum posts give theimagesthey show.- Forum subscriptions (#53):
subscribedinforumDiscussionsanddiscussion;startDiscussionreturns the resultingpinnedandsubscribed, andreplyToPostsubscribedwhen asked to subscribe; newsetDiscussionSubscription(alsomoodle set-discussion-subscription).
Fixes
courseStructure:availableToMeis true for a teacher on a hidden or restricted activity they can open (#50).setEnrolmentMethodswitches a method on or off as Moodle's methods page does, and refuses with a clearEnrolmentErrorwhat Moodle doesn't let the person change (a teacher on the manual method), instead of failing on a missing form field; a request for the state a method already has succeeds (#51).
Changes a program may notice
uploadToDraft(internals) returns the draft URLs of the files it uploaded.readImages(internals) and theImageItemtype are new.
Checked against the Moodle 5.2 sandbox: the courses, building, forums and participants files of the regression suite pass (88 tests).
v0.5.1
Changes
- The
Moodleobject's methods are documented one by one. Each method (moodle.listCourses(),moodle.gradeSubmission()…) now shows its real signature and its function's doc comment, in the API reference and when an editor hovers over it. Before, the reference showed them as(...rest) => Promisewith no description, and editors lost the doc comment. No behaviour changes: the types are the same, written out. submitSurveyas a method now offers both its signatures, as the function does (it only offered the last one).QuizChange, the result of the quiz structure functions (editQuizSlot,removeQuizSlot,setQuizPages,addQuizSection), is exported.
v0.5.0
Managing a course in full: everything a teacher does to run a course, beyond reading it.
New
People (participants)
- Enrol, unenrol and set roles:
potentialUsers,enrolUsers,unenrolUser,setUserRoles(#28). - Groups and groupings with their members:
createGroup,updateGroup,deleteGroup,setGroupMembers,groupings,createGrouping,deleteGrouping,setGroupingGroups(#29). - Enrolment methods (self enrolment with a key, guest):
enrolmentMethods,setEnrolmentMethod(#44). - One message to several participants:
messageParticipants(#47).
Building a course (activities)
- Replace, add or remove a file resource's or folder's files:
updateActivity(id, { files: { add, remove, replace } })(#31). - Sections' descriptions:
summaryinaddSection,editSectionandcourseStructure({ details })(#32). - Access restrictions and completion conditions in plain terms:
restrictionsandcompletionincreateActivity,updateActivity,editSectionandactivitySettings(#33). - Copy, back up, restore and import courses:
copyCourse,backupCourse,restoreCourse,importActivities(#42); reset one:resetCourse(#43).
Quizzes and question banks
- Grade by hand (essays):
questionsToGrade,gradeQuizQuestion(#30). - A quiz's structure:
editQuizSlot,removeQuizSlot,setQuizPages,addQuizSection(#34). - Edit and delete bank questions:
updateQuestion,deleteQuestions,setQuestionStatus(#35).
Grading and grades
- The gradebook:
setGrade(override, exclude, manual items),addGradeItem,updateGradeCategory,deleteGradeCategory,deleteGradeItem(#37); export:exportGrades(#38). - Whole-forum grading and ratings:
forumGrades,gradeForumUser,rate(#46); workshops:workshopInfo,setWorkshopPhase,assessWorkshopSubmission(#48). - Assignments: feedback files (
gradeSubmission'sfeedbackFiles) (#40); group submissions and the marking workflow:setWorkflowState,allocateMarker,workflowState/applyToGroupingradeSubmission(#41).
Forums, calendar, reports
- Edit and delete posts:
editPost,deletePost(#39). - Change an event:
updateEvent,calendarEvent(#36). - Logs and reports:
courseLogs,activityReport,participationReport(#45).
The moodle command (command line)
moodle shell: commands typed one after another on one browser (#24).moodle open/moodle close: a background browser separate commands share, behind a token (ADR-014); for programs,connectBrowserManagerandbackgroundBrowser(#25).- Every new function is also a command (132 in all).
New error classes, with their codes: EnrolmentError, ForumError, ReportError, BackupError, WorkshopError, and the generic background-browser and browser-busy.
Fixes
- Security: when a request to Moodle timed out, the error's message carried the session cookie and the
sesskey(Playwright lists the request's headers). Errors now say[redacted]instead, so they can be logged as they are. setFields(and so every form with a rich text editor, such as forum posts) raced TinyMCE: content set while the editor was still starting threw. It now waits for the editor.userByIdgivesundefinedfor a deleted account, which Moodle still returns to an administrator, renamed.createEventsaved a course event with the course's first group, which hid it from the teacher's upcoming events.createActivityrefused an H5P activity's package, and gave noactivityIdfor a question bank.h5pActivitysaid a student's tracked activity wasn't tracked before their first attempt.getChoice,answerChoiceandgroupingstook Moodle's error page for an empty result; they now throw with Moodle's reason.importQuestionsinto a given category failed on a field Moodle disables.ratefound no rating menu for glossary entries and database records.allocateMarkerandsetWorkflowStatenow refuse an assignment without marker allocation or the marking workflow;grantExtensionandsubmitForGradingthrow when Moodle refuses, instead of reporting success.
All found by a new regression suite that runs every exported function against a real Moodle 5.2 (#49): npm run test:sandbox, on demand.
Changes a program may notice
QuizSlothasslot(its place in the quiz), andquizStructureandaddQuestionsToQuizreturnsections.dateSelectorValuestakes an optional time zone;uploadToDrafttakes{ from, to }items and{ overwrite }.CourseChangemay carry the activity'sfiles;Questionhascomment;SubmissionhasfeedbackFiles,group,workflowStateandmarkers.
v0.4.1
Fixes
conversation()gives asmemberseveryone in the conversation but the person, whether they wrote in it or not, asconversations()does (the person alone in their own conversation). It used to give only those who had written one of the messages, so someone who had only received messages was missing (#27).
v0.4.0
New
SCORM activities (docs):
scorm(activityId): a SCORM as the person sees it: attempts allowed and made, grading method, the grade reported and each attempt's, and its SCOs with the person's status (not-attempted,incomplete,completed,passed,failed…) and score. Never starts an attempt. Alsomoodle scorm <id>.openScorm(activityId, { scoId?, newAttempt? }): opens Moodle's player on the working tab, continuing the last attempt or starting a new one, and resolves once the SCO has loaded; returns the attempt, the SCO, the SCORM version and the URL.createActivity({ type: "scorm", files: [package] })creates a SCORM from its package.ScormError, codescorm.
The driver never reports a SCORM's results for the person: they come only from doing it in the player.
Internal
npm publishruns typecheck, lint, the docs' examples and the tests in Docker first.
v0.3.1
A fix for the moodle command.
Install
npm i -g https://github.com/falkenslab/moodle-driver/releases/download/v0.3.1/falkenslab-moodle-driver-0.3.1.tgz # the moodle command
npm install https://github.com/falkenslab/moodle-driver/releases/download/v0.3.1/falkenslab-moodle-driver-0.3.1.tgz playwright-core # the libraryFixes
moodle public-site-infoneeds no profile: it asks what anyone can, without a browser, and 0.3.0 refused it on a site with no login yet.
No changes to the library's API. See v0.3.0 for what's new in 0.3.
v0.3.0
A command for the whole API, Moodle's services over plain HTTP, and a friendlier API. Several changes break 0.2 programs: see Breaking changes.
Install
Not on npm yet: the package is attached to this release, built.
npm i -g https://github.com/falkenslab/moodle-driver/releases/download/v0.3.0/falkenslab-moodle-driver-0.3.0.tgz # the moodle command
npm install https://github.com/falkenslab/moodle-driver/releases/download/v0.3.0/falkenslab-moodle-driver-0.3.0.tgz playwright-core # the libraryNew
- The
moodlecommand. Every method of the Moodle object from a terminal:moodle login --site …once, thenmoodle list-courses,moodle my-grades 3 --json,moodle grade-submission 35 7 --grade 8.5 --notify… 81 commands generated from the API's types, so none is missing; tables at a terminal, JSON for scripts, errors with their code and exit status,--allowto limit the data categories,--showto watch the browser. See the command line and the commands. - Sites and accounts. The command keeps everything in
~/.moodle-driveron every system: a folder per site, a named profile per account (moodle login --as student), the site's default in itssite.json; a first login names its profile after the Moodle username.moodle siteslists them,moodle forgetdeletes one.siteProfileDir(site)lets a program share the command's sessions. - Moodle's services over HTTP.
callServiceposts tolib/ajax/service.phpwith the profile's session and its sesskey, without a tab; the kept service tab is gone. Pages and forms stay in tabs: measured, Moodle's server takes most of their time (ADR-011).fetchSitePage,readMoodleConfigandhtmlSessionFactsin/internalsread a page over HTTP. - Friendlier calls:
activityContent(activity)takes an activity as the course structure gives it;deadlines()andlogIn()need no options object;gradeSubmissiontakes a numeric grade; the Moodle object works withawait using. - Documentation: 17 complete, commented use-case examples (student, teacher, programs), a glossary of Moodle's terms, diagrams, a new logo, and every example compiled against the code (
npm run docs:check, also in CI). The site now lives at https://falkenslab.github.io/moodle-driver/ (no/docs).
Breaking changes
| Was (0.2) | Now |
|---|---|
MoodleDriverError |
MoodleError (same codes and subclasses) |
moodle-driver-session.json in the profile |
moodle-session.json: profiles from 0.2 log in once more |
courseStructure(id) → { course, sections } |
the sections, CourseSection[] |
assignmentSubmissions(id) → { assignment, submissions } |
the submissions, Submission[] |
callService from a kept tab |
over HTTP; nothing to change in calls |
Fixes
- The Moodle object's
answerQuizQuestionsis typed withchanged, which the function returned already. pause()no longer reports the same second twice when a timer fires a millisecond early.
Internal
- ADR-011 (services over HTTP, pages in tabs, with the timings), ADR-012 (the command in the package), ADR-013 (one home for sites and sessions).
scripts/benchmark-reads.mtsmeasures reads on any site;scripts/generate-cli-commands.mjskeeps the command table in step with the types.
Documentation: https://falkenslab.github.io/moodle-driver/
v0.2.0
A release that settles the shape of the API before more programs depend on it. Most of it is breaking: read Breaking changes before upgrading.
New
- The Moodle object's defaults.
createMoodle()takestrail,onProgress,loginTimeoutMsanddownloadDir, so they aren't repeated on every call; any call can still give its own.logIn()needs no arguments (5 minutes by default). - Surveys find their module.
getSurvey(activityId)andsubmitSurvey(activityId, responses)work out whether it's a feedback or a questionnaire when not told ({ type }still accepted). submitForGrading(activityId, { acceptStatement }), an options object instead of a bare boolean (still accepted).MoodleDriverError. Every error the driver throws extends it and carries a stablecode(not-logged-in,survey,form…, and generic ones:invalid-input,not-found,not-available,outside-site,unexpected-page,logout-failed,chromium-install-failed). Match errors by class,nameorcode, never by message.@falkenslab/moodle-driver/internals, a second entry point with the primitives the operations are built from:callService,readPageConfig,openSitePage,readSessionFacts,pageIsLoggedIn,moodleErrorMessage,openForm,formValue,setFields,submitForm,uploadToDraft,dateSelectorValues,dateSelectorDate,inPage,readTexts,readMedia,decodeEntities,readingSeconds,pause,editCourse,renameActivity,renameSection,courseState,activityDetails,readOwnProfile,readWwwroot,fileNameFor.moodle.readAttempt(attemptId), now a method too.
Breaking changes
The main entry is curated. It exports the Moodle object, the operations, their types and errors. The primitives moved to @falkenslab/moodle-driver/internals (list above): change the import. The areas' parsers and single steps are no longer exported at all: readQuizView, readQuestions, readNavigation, startAttempt, saveAnswers, finishAttempt, readQuizReport, readQuizSlots, assignmentIds, readAssignmentIds, listParticipants, participantDates, gradingPanelHtml, fillGradingForm, panelGrade, submitGradingForm, readSubmissionContent, readGradingDefinition, readStudentAssignment, listDiscussions, discussionPosts, forumInstanceId, readDiscussionRows, structureOf, assignmentsOf, toCourse, isoDate, overviewField, readFormFields, surveyType, readSurveyPage, readChoice, readLessonPage, readH5PAttempts, readGradeRows, readGraderReport, readGradebookSetup, readBankQuestions, readParticipantRows, readCompletionReport, waitForLogin, followPages, parseServiceResponse, courseFormat, and the raw types MoodleCourse, MoodleParticipant, MoodleParticipantDates, AssignmentIds, DiscussionRow, MoodlePost, MoodleUser. Use the operations (startQuizAttempt, assignmentSubmissions, forumDiscussions, courseStructure…) or the primitives instead.
Activity ids are activityId. Every parameter taking an activity's course module id is named activityId, whatever the module (it was cmid, forumCmid, bankCmid or quizId). Positional calls don't change; code reading the names (wrappers, docs) does.
Results name ids one way: a thing's own id is id, the ids it refers to are <thing>Id.
| Was | Now |
|---|---|
QuizSlot.slotId |
id |
quizInfo().attempts[].attemptId |
id |
QuizAttemptResult.attemptId |
id |
H5PAttempt.attemptId |
id |
GradeColumn.itemId |
id (grades still refer to it as itemId) |
QuestionBank.cmid |
id |
CourseChange.newId |
activityId (duplicateActivity) or sectionId (addSection) |
discussion().discussion.forumId (the instance id) |
forumInstanceId |
userById returns a User in the driver's terms (fullName, firstAccess and lastAccess as ISO 8601 dates, left out for never), no longer Moodle's raw record (fullname, Unix seconds).
Every write returns { changed, …state }:
| Function | Was | Now |
|---|---|---|
saveSubmission, submitForGrading |
StudentAssignment |
{ changed, assignment } |
answerChoice |
Choice |
{ changed, choice } |
submitSurvey |
{ messages } |
{ changed, messages } |
startQuizAttempt, answerQuizQuestions, finishQuizAttempt, answerLessonPage |
their objects | the same, with changed |
Plain Errors are now MoodleDriverErrors with a code (relative paths, missing files, a forum without attachments, content for a module without it, a failed logout, a file outside the site…). instanceof Error still holds.
The session cookies file is moodle-driver-session.json (SESSION_COOKIES_FILE), no longer moodle-mcp-session-cookies.json. Profiles from before keep their persistent cookies but lose Moodle's session cookie: the person logs in once more.
The object is leaner: it no longer has the pieces the operations are built from (assignmentIds, gradingPanelHtml, startAttempt, courseState, editCourse and the like). It keeps callService, openSitePage and openForm.
Internal
- The source is one folder per Moodle area (
src/areas/<area>/), withsrc/core/for what they're built on andsrc/browser/for the browser; the tests mirror it. - ADR-010 records the curated entry and
/internals; the conventions record the naming and result rules.
Documentation: https://falkenslab.github.io/moodle-driver/