@falkenslab/moodle-driver 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (297) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -2
  3. package/dist/areas/activities/build.d.ts +98 -0
  4. package/dist/areas/activities/build.js +246 -0
  5. package/dist/areas/activities/build.js.map +1 -0
  6. package/dist/areas/activities/content.d.ts +53 -0
  7. package/dist/areas/activities/content.js +113 -0
  8. package/dist/areas/activities/content.js.map +1 -0
  9. package/dist/areas/assignments/grader.d.ts +68 -0
  10. package/dist/areas/assignments/grader.js +169 -0
  11. package/dist/areas/assignments/grader.js.map +1 -0
  12. package/dist/areas/assignments/grading.d.ts +86 -0
  13. package/dist/areas/assignments/grading.js +162 -0
  14. package/dist/areas/assignments/grading.js.map +1 -0
  15. package/dist/areas/assignments/student.d.ts +57 -0
  16. package/dist/areas/assignments/student.js +115 -0
  17. package/dist/areas/assignments/student.js.map +1 -0
  18. package/dist/areas/calendar/calendar.d.ts +43 -0
  19. package/dist/areas/calendar/calendar.js +84 -0
  20. package/dist/areas/calendar/calendar.js.map +1 -0
  21. package/dist/areas/completion/completion.d.ts +50 -0
  22. package/dist/areas/completion/completion.js +112 -0
  23. package/dist/areas/completion/completion.js.map +1 -0
  24. package/dist/areas/courses/list.d.ts +59 -0
  25. package/dist/areas/courses/list.js +74 -0
  26. package/dist/areas/courses/list.js.map +1 -0
  27. package/dist/areas/courses/overview.d.ts +83 -0
  28. package/dist/areas/courses/overview.js +137 -0
  29. package/dist/areas/courses/overview.js.map +1 -0
  30. package/dist/areas/courses/state.d.ts +51 -0
  31. package/dist/areas/courses/state.js +45 -0
  32. package/dist/areas/courses/state.js.map +1 -0
  33. package/dist/areas/courses/structure.d.ts +89 -0
  34. package/dist/areas/courses/structure.js +104 -0
  35. package/dist/areas/courses/structure.js.map +1 -0
  36. package/dist/areas/forums/discussions.d.ts +57 -0
  37. package/dist/areas/forums/discussions.js +119 -0
  38. package/dist/areas/forums/discussions.js.map +1 -0
  39. package/dist/areas/forums/posts.d.ts +83 -0
  40. package/dist/areas/forums/posts.js +114 -0
  41. package/dist/areas/forums/posts.js.map +1 -0
  42. package/dist/areas/grades/gradebook.d.ts +104 -0
  43. package/dist/areas/grades/gradebook.js +250 -0
  44. package/dist/areas/grades/gradebook.js.map +1 -0
  45. package/dist/areas/grades/user-report.d.ts +26 -0
  46. package/dist/areas/grades/user-report.js +71 -0
  47. package/dist/areas/grades/user-report.js.map +1 -0
  48. package/dist/areas/h5p/h5p.d.ts +33 -0
  49. package/dist/areas/h5p/h5p.js +104 -0
  50. package/dist/areas/h5p/h5p.js.map +1 -0
  51. package/dist/areas/lessons/lessons.d.ts +60 -0
  52. package/dist/areas/lessons/lessons.js +145 -0
  53. package/dist/areas/lessons/lessons.js.map +1 -0
  54. package/dist/areas/messages/messages.d.ts +68 -0
  55. package/dist/areas/messages/messages.js +84 -0
  56. package/dist/areas/messages/messages.js.map +1 -0
  57. package/dist/areas/participants/participants.d.ts +57 -0
  58. package/dist/areas/participants/participants.js +135 -0
  59. package/dist/areas/participants/participants.js.map +1 -0
  60. package/dist/areas/questions/banks.d.ts +83 -0
  61. package/dist/areas/questions/banks.js +239 -0
  62. package/dist/areas/questions/banks.js.map +1 -0
  63. package/dist/areas/quizzes/attempts.d.ts +123 -0
  64. package/dist/areas/quizzes/attempts.js +300 -0
  65. package/dist/areas/quizzes/attempts.js.map +1 -0
  66. package/dist/areas/quizzes/results.d.ts +36 -0
  67. package/dist/areas/quizzes/results.js +93 -0
  68. package/dist/areas/quizzes/results.js.map +1 -0
  69. package/dist/areas/quizzes/structure.d.ts +42 -0
  70. package/dist/areas/quizzes/structure.js +100 -0
  71. package/dist/areas/quizzes/structure.js.map +1 -0
  72. package/dist/areas/site/info.d.ts +25 -0
  73. package/dist/areas/site/info.js +45 -0
  74. package/dist/areas/site/info.js.map +1 -0
  75. package/dist/areas/site/users.d.ts +56 -0
  76. package/dist/areas/site/users.js +89 -0
  77. package/dist/areas/site/users.js.map +1 -0
  78. package/dist/areas/surveys/choice.d.ts +38 -0
  79. package/dist/areas/surveys/choice.js +105 -0
  80. package/dist/areas/surveys/choice.js.map +1 -0
  81. package/dist/areas/surveys/surveys.d.ts +51 -0
  82. package/dist/areas/surveys/surveys.js +201 -0
  83. package/dist/areas/surveys/surveys.js.map +1 -0
  84. package/dist/browser/chromium.d.ts +15 -0
  85. package/dist/browser/chromium.js +53 -0
  86. package/dist/browser/chromium.js.map +1 -0
  87. package/dist/browser/cookies.d.ts +11 -0
  88. package/dist/browser/cookies.js +31 -0
  89. package/dist/browser/cookies.js.map +1 -0
  90. package/dist/browser/manager.d.ts +57 -0
  91. package/dist/browser/manager.js +177 -0
  92. package/dist/browser/manager.js.map +1 -0
  93. package/dist/browser.d.ts +53 -0
  94. package/dist/browser.js +167 -0
  95. package/dist/browser.js.map +1 -0
  96. package/dist/categories.d.ts +28 -0
  97. package/dist/categories.js +222 -0
  98. package/dist/categories.js.map +1 -0
  99. package/dist/chromiumInstaller.d.ts +15 -0
  100. package/dist/chromiumInstaller.js +52 -0
  101. package/dist/chromiumInstaller.js.map +1 -0
  102. package/dist/cli/args.d.ts +33 -0
  103. package/dist/cli/args.js +194 -0
  104. package/dist/cli/args.js.map +1 -0
  105. package/dist/cli/commands.generated.d.ts +4 -0
  106. package/dist/cli/commands.generated.js +2203 -0
  107. package/dist/cli/commands.generated.js.map +1 -0
  108. package/dist/cli/config.d.ts +46 -0
  109. package/dist/cli/config.js +107 -0
  110. package/dist/cli/config.js.map +1 -0
  111. package/dist/cli/main.d.ts +2 -0
  112. package/dist/cli/main.js +19 -0
  113. package/dist/cli/main.js.map +1 -0
  114. package/dist/cli/output.d.ts +4 -0
  115. package/dist/cli/output.js +71 -0
  116. package/dist/cli/output.js.map +1 -0
  117. package/dist/cli/run.d.ts +15 -0
  118. package/dist/cli/run.js +194 -0
  119. package/dist/cli/run.js.map +1 -0
  120. package/dist/cli/spec.d.ts +42 -0
  121. package/dist/cli/spec.js +4 -0
  122. package/dist/cli/spec.js.map +1 -0
  123. package/dist/client.d.ts +217 -0
  124. package/dist/client.js +179 -0
  125. package/dist/client.js.map +1 -0
  126. package/dist/core/ajax.d.ts +25 -0
  127. package/dist/core/ajax.js +85 -0
  128. package/dist/core/ajax.js.map +1 -0
  129. package/dist/core/editor.d.ts +18 -0
  130. package/dist/core/editor.js +51 -0
  131. package/dist/core/editor.js.map +1 -0
  132. package/dist/core/errors.d.ts +12 -0
  133. package/dist/core/errors.js +10 -0
  134. package/dist/core/errors.js.map +1 -0
  135. package/dist/core/files.d.ts +16 -0
  136. package/dist/core/files.js +58 -0
  137. package/dist/core/files.js.map +1 -0
  138. package/dist/core/forms.d.ts +45 -0
  139. package/dist/core/forms.js +200 -0
  140. package/dist/core/forms.js.map +1 -0
  141. package/dist/core/http.d.ts +18 -0
  142. package/dist/core/http.js +22 -0
  143. package/dist/core/http.js.map +1 -0
  144. package/dist/core/inPage.d.ts +8 -0
  145. package/dist/core/inPage.js +10 -0
  146. package/dist/core/inPage.js.map +1 -0
  147. package/dist/core/login.d.ts +40 -0
  148. package/dist/core/login.js +83 -0
  149. package/dist/core/login.js.map +1 -0
  150. package/dist/core/pace.d.ts +4 -0
  151. package/dist/core/pace.js +21 -0
  152. package/dist/core/pace.js.map +1 -0
  153. package/dist/core/session.d.ts +55 -0
  154. package/dist/core/session.js +157 -0
  155. package/dist/core/session.js.map +1 -0
  156. package/dist/core/site.d.ts +8 -0
  157. package/dist/core/site.js +36 -0
  158. package/dist/core/site.js.map +1 -0
  159. package/dist/core/text.d.ts +21 -0
  160. package/dist/core/text.js +113 -0
  161. package/dist/core/text.js.map +1 -0
  162. package/dist/home.d.ts +19 -0
  163. package/dist/home.js +54 -0
  164. package/dist/home.js.map +1 -0
  165. package/dist/index.d.ts +40 -0
  166. package/dist/index.js +51 -0
  167. package/dist/index.js.map +1 -0
  168. package/dist/internals.d.ts +11 -0
  169. package/dist/internals.js +15 -0
  170. package/dist/internals.js.map +1 -0
  171. package/dist/moodle/activities.d.ts +94 -0
  172. package/dist/moodle/activities.js +240 -0
  173. package/dist/moodle/activities.js.map +1 -0
  174. package/dist/moodle/ajax.d.ts +22 -0
  175. package/dist/moodle/ajax.js +91 -0
  176. package/dist/moodle/ajax.js.map +1 -0
  177. package/dist/moodle/assign.d.ts +65 -0
  178. package/dist/moodle/assign.js +162 -0
  179. package/dist/moodle/assign.js.map +1 -0
  180. package/dist/moodle/calendar.d.ts +41 -0
  181. package/dist/moodle/calendar.js +80 -0
  182. package/dist/moodle/calendar.js.map +1 -0
  183. package/dist/moodle/choice.d.ts +33 -0
  184. package/dist/moodle/choice.js +100 -0
  185. package/dist/moodle/choice.js.map +1 -0
  186. package/dist/moodle/completion.d.ts +48 -0
  187. package/dist/moodle/completion.js +108 -0
  188. package/dist/moodle/completion.js.map +1 -0
  189. package/dist/moodle/content.d.ts +43 -0
  190. package/dist/moodle/content.js +105 -0
  191. package/dist/moodle/content.js.map +1 -0
  192. package/dist/moodle/course.d.ts +51 -0
  193. package/dist/moodle/course.js +45 -0
  194. package/dist/moodle/course.js.map +1 -0
  195. package/dist/moodle/courses.d.ts +57 -0
  196. package/dist/moodle/courses.js +70 -0
  197. package/dist/moodle/courses.js.map +1 -0
  198. package/dist/moodle/editor.d.ts +18 -0
  199. package/dist/moodle/editor.js +50 -0
  200. package/dist/moodle/editor.js.map +1 -0
  201. package/dist/moodle/files.d.ts +16 -0
  202. package/dist/moodle/files.js +57 -0
  203. package/dist/moodle/files.js.map +1 -0
  204. package/dist/moodle/forms.d.ts +44 -0
  205. package/dist/moodle/forms.js +199 -0
  206. package/dist/moodle/forms.js.map +1 -0
  207. package/dist/moodle/forum.d.ts +55 -0
  208. package/dist/moodle/forum.js +115 -0
  209. package/dist/moodle/forum.js.map +1 -0
  210. package/dist/moodle/forumPosts.d.ts +83 -0
  211. package/dist/moodle/forumPosts.js +113 -0
  212. package/dist/moodle/forumPosts.js.map +1 -0
  213. package/dist/moodle/gradebook.d.ts +102 -0
  214. package/dist/moodle/gradebook.js +246 -0
  215. package/dist/moodle/gradebook.js.map +1 -0
  216. package/dist/moodle/grades.d.ts +24 -0
  217. package/dist/moodle/grades.js +67 -0
  218. package/dist/moodle/grades.js.map +1 -0
  219. package/dist/moodle/grading.d.ts +92 -0
  220. package/dist/moodle/grading.js +161 -0
  221. package/dist/moodle/grading.js.map +1 -0
  222. package/dist/moodle/h5p.d.ts +31 -0
  223. package/dist/moodle/h5p.js +100 -0
  224. package/dist/moodle/h5p.js.map +1 -0
  225. package/dist/moodle/inPage.d.ts +8 -0
  226. package/dist/moodle/inPage.js +10 -0
  227. package/dist/moodle/inPage.js.map +1 -0
  228. package/dist/moodle/lesson.d.ts +57 -0
  229. package/dist/moodle/lesson.js +140 -0
  230. package/dist/moodle/lesson.js.map +1 -0
  231. package/dist/moodle/login.d.ts +37 -0
  232. package/dist/moodle/login.js +77 -0
  233. package/dist/moodle/login.js.map +1 -0
  234. package/dist/moodle/messages.d.ts +66 -0
  235. package/dist/moodle/messages.js +80 -0
  236. package/dist/moodle/messages.js.map +1 -0
  237. package/dist/moodle/overview.d.ts +83 -0
  238. package/dist/moodle/overview.js +137 -0
  239. package/dist/moodle/overview.js.map +1 -0
  240. package/dist/moodle/pace.d.ts +4 -0
  241. package/dist/moodle/pace.js +19 -0
  242. package/dist/moodle/pace.js.map +1 -0
  243. package/dist/moodle/participants.d.ts +55 -0
  244. package/dist/moodle/participants.js +131 -0
  245. package/dist/moodle/participants.js.map +1 -0
  246. package/dist/moodle/questionBank.d.ts +81 -0
  247. package/dist/moodle/questionBank.js +235 -0
  248. package/dist/moodle/questionBank.js.map +1 -0
  249. package/dist/moodle/quiz.d.ts +117 -0
  250. package/dist/moodle/quiz.js +291 -0
  251. package/dist/moodle/quiz.js.map +1 -0
  252. package/dist/moodle/quizResults.d.ts +36 -0
  253. package/dist/moodle/quizResults.js +93 -0
  254. package/dist/moodle/quizResults.js.map +1 -0
  255. package/dist/moodle/quizStructure.d.ts +42 -0
  256. package/dist/moodle/quizStructure.js +100 -0
  257. package/dist/moodle/quizStructure.js.map +1 -0
  258. package/dist/moodle/session.d.ts +39 -0
  259. package/dist/moodle/session.js +99 -0
  260. package/dist/moodle/session.js.map +1 -0
  261. package/dist/moodle/site.d.ts +8 -0
  262. package/dist/moodle/site.js +36 -0
  263. package/dist/moodle/site.js.map +1 -0
  264. package/dist/moodle/siteInfo.d.ts +25 -0
  265. package/dist/moodle/siteInfo.js +45 -0
  266. package/dist/moodle/siteInfo.js.map +1 -0
  267. package/dist/moodle/structure.d.ts +94 -0
  268. package/dist/moodle/structure.js +103 -0
  269. package/dist/moodle/structure.js.map +1 -0
  270. package/dist/moodle/submission.d.ts +48 -0
  271. package/dist/moodle/submission.js +109 -0
  272. package/dist/moodle/submission.js.map +1 -0
  273. package/dist/moodle/survey.d.ts +47 -0
  274. package/dist/moodle/survey.js +197 -0
  275. package/dist/moodle/survey.js.map +1 -0
  276. package/dist/moodle/text.d.ts +21 -0
  277. package/dist/moodle/text.js +113 -0
  278. package/dist/moodle/text.js.map +1 -0
  279. package/dist/moodle/user.d.ts +54 -0
  280. package/dist/moodle/user.js +78 -0
  281. package/dist/moodle/user.js.map +1 -0
  282. package/dist/options.d.ts +30 -0
  283. package/dist/options.js +35 -0
  284. package/dist/options.js.map +1 -0
  285. package/dist/sessionCookies.d.ts +11 -0
  286. package/dist/sessionCookies.js +31 -0
  287. package/dist/sessionCookies.js.map +1 -0
  288. package/dist/testing/fakeMoodle.d.ts +55 -0
  289. package/dist/testing/fakeMoodle.js +103 -0
  290. package/dist/testing/fakeMoodle.js.map +1 -0
  291. package/dist/testing/gradingPanel.d.ts +6 -0
  292. package/dist/testing/gradingPanel.js +22 -0
  293. package/dist/testing/gradingPanel.js.map +1 -0
  294. package/dist/testing/index.d.ts +5 -0
  295. package/dist/testing/index.js +18 -0
  296. package/dist/testing/index.js.map +1 -0
  297. package/package.json +77 -4
@@ -0,0 +1,15 @@
1
+ // @falkenslab/moodle-driver/internals: the primitives the operations are built from, for a program
2
+ // that goes lower level than them (a service the driver doesn't wrap, a form of its own, a page read
3
+ // by hand). Stable as the main entry is, but closer to Moodle: they take pages and form names.
4
+ export { callService, readPageConfig } from "./core/ajax.js";
5
+ export { openSitePage, fetchSitePage, readSessionFacts, htmlSessionFacts, readMoodleConfig, isLoggedIn, pageIsLoggedIn, moodleErrorMessage } from "./core/session.js";
6
+ export { readOwnProfile } from "./core/login.js";
7
+ export { openForm, formValue, setFields, submitForm, uploadToDraft, dateSelectorValues, dateSelectorDate } from "./core/forms.js";
8
+ export { inPage } from "./core/inPage.js";
9
+ export { decodeEntities, readTexts, readMedia } from "./core/text.js";
10
+ export { readingSeconds, pause } from "./core/pace.js";
11
+ export { editCourse, renameActivity, renameSection } from "./core/editor.js";
12
+ export { readWwwroot } from "./core/site.js";
13
+ export { fileNameFor } from "./core/files.js";
14
+ export { courseState, activityDetails } from "./areas/courses/state.js";
15
+ //# sourceMappingURL=internals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internals.js","sourceRoot":"","sources":["../src/internals.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,qGAAqG;AACrG,+FAA+F;AAC/F,OAAO,EAAE,WAAW,EAAE,cAAc,EAAmB,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAqB,MAAM,mBAAmB,CAAC;AACzL,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAClI,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAqB,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,eAAe,EAA0C,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,94 @@
1
+ import type { BrowserContext, Page } from "playwright-core";
2
+ import { type FieldValue } from "./forms.js";
3
+ import { type CourseSection } from "./structure.js";
4
+ /** "stealth": hidden from the course page but available by link, where the site allows it. */
5
+ export declare const ACTIVITY_VISIBILITIES: readonly ["shown", "hidden", "stealth"];
6
+ export type ActivityVisibility = (typeof ACTIVITY_VISIBILITIES)[number];
7
+ /** The common inputs of an activity's form; anything else goes in `settings` by its form field name. */
8
+ export interface ActivityFields {
9
+ name?: string;
10
+ /** The description (HTML), shown on the activity's page. */
11
+ description?: string;
12
+ /** A page's content, or a label's text (HTML). */
13
+ content?: string;
14
+ /** A URL resource's target. */
15
+ url?: string;
16
+ visibility?: ActivityVisibility;
17
+ settings?: Record<string, FieldValue>;
18
+ }
19
+ /** What a change did, its new id when it created something, and the course afterwards. */
20
+ export interface CourseChange {
21
+ changed: string;
22
+ activityId?: number;
23
+ newId?: number;
24
+ url?: string;
25
+ sections: CourseSection[];
26
+ }
27
+ /**
28
+ * Creates an activity or resource of any type at the end of a section, through Moodle's own form,
29
+ * hidden from students unless `visibility` says otherwise. `files` are local paths uploaded to a file
30
+ * resource or folder.
31
+ */
32
+ export declare function createActivity(context: BrowserContext, siteUrl: string, input: ActivityFields & {
33
+ courseId: number;
34
+ sectionId: number;
35
+ type: string;
36
+ files?: string[];
37
+ }): Promise<CourseChange>;
38
+ /** Changes an existing activity through its form: only what's given changes; files stay as they are. */
39
+ export declare function updateActivity(context: BrowserContext, siteUrl: string, cmid: number, input: ActivityFields): Promise<CourseChange>;
40
+ /** Renames, shows or hides, or moves an activity (to a section's end, or before another one). */
41
+ export declare function editActivity(context: BrowserContext, siteUrl: string, courseId: number, cmid: number, { name, visibility, sectionId, beforeActivityId }: {
42
+ name?: string;
43
+ visibility?: ActivityVisibility;
44
+ sectionId?: number;
45
+ beforeActivityId?: number;
46
+ }): Promise<CourseChange>;
47
+ export declare function duplicateActivity(context: BrowserContext, siteUrl: string, courseId: number, cmid: number): Promise<CourseChange>;
48
+ export declare function deleteActivity(context: BrowserContext, siteUrl: string, courseId: number, cmid: number): Promise<CourseChange>;
49
+ /** Adds a section after another one (at the end without it), named when a name is given. */
50
+ export declare function addSection(context: BrowserContext, siteUrl: string, courseId: number, { name, afterSectionId }?: {
51
+ name?: string;
52
+ afterSectionId?: number;
53
+ }): Promise<CourseChange>;
54
+ /** Renames, shows or hides, or moves a section after another one. */
55
+ export declare function editSection(context: BrowserContext, siteUrl: string, courseId: number, sectionId: number, { name, visible, afterSectionId }: {
56
+ name?: string;
57
+ visible?: boolean;
58
+ afterSectionId?: number;
59
+ }): Promise<CourseChange>;
60
+ /** Deletes a section and every activity in it, with their participants' data. */
61
+ export declare function deleteSection(context: BrowserContext, siteUrl: string, courseId: number, sectionId: number): Promise<CourseChange>;
62
+ /** One field of a Moodle form, as it is now: its name (what `settings` takes), label, value and options. */
63
+ export interface FormField {
64
+ name: string;
65
+ type: "text" | "textarea" | "select" | "checkbox" | "radio" | "number" | "date" | "other";
66
+ label?: string;
67
+ /** Text, the selected option (several for a multiple select), the checked radio's value. */
68
+ value?: string | string[];
69
+ checked?: boolean;
70
+ options?: {
71
+ value: string;
72
+ label: string;
73
+ }[];
74
+ /** Disabled until another field enables it; Moodle doesn't save it as it is. */
75
+ disabled?: boolean;
76
+ }
77
+ /** The fields of a Moodle form in `page`, internal ones (sesskey, ids, buttons) left out, editors' HTML included. */
78
+ export declare function readFormFields(page: Page): Promise<FormField[]>;
79
+ /** Nobody can see that activity's settings: not an activity, or not the person's to edit. */
80
+ export declare class ActivitySettingsError extends Error {
81
+ name: string;
82
+ }
83
+ /**
84
+ * An activity's settings as its form holds them, without submitting it: its module, name and
85
+ * description, and every field by the name `updateActivity` takes in `settings`. Teachers only.
86
+ */
87
+ export declare function activitySettings(context: BrowserContext, siteUrl: string, cmid: number): Promise<{
88
+ id: number;
89
+ module: string;
90
+ courseId: number;
91
+ name: string;
92
+ description: string;
93
+ fields: FormField[];
94
+ }>;
@@ -0,0 +1,240 @@
1
+ import { courseState } from "./course.js";
2
+ import { editCourse, renameActivity, renameSection } from "./editor.js";
3
+ import { checkLocalFiles } from "./files.js";
4
+ import { formValue, openForm, setFields, submitForm, uploadToDraft } from "./forms.js";
5
+ import { inPage } from "./inPage.js";
6
+ import { moodleErrorMessage, openSitePage } from "./session.js";
7
+ import { structureOf } from "./structure.js";
8
+ /** "stealth": hidden from the course page but available by link, where the site allows it. */
9
+ export const ACTIVITY_VISIBILITIES = ["shown", "hidden", "stealth"];
10
+ const FORM_VISIBILITY = { shown: 1, hidden: 0, stealth: -1 };
11
+ const VISIBILITY_ACTIONS = { shown: "cm_show", hidden: "cm_hide", stealth: "cm_stealth" };
12
+ /** The form values the common inputs stand for, given the activity's type. */
13
+ function fieldsFor(type, input) {
14
+ const values = {};
15
+ if (input.name !== undefined)
16
+ values.name = input.name;
17
+ if (input.description !== undefined)
18
+ values["introeditor[text]"] = input.description;
19
+ if (input.content !== undefined) {
20
+ // A label's text is its description field; a page has its own content editor.
21
+ if (type === "label")
22
+ values["introeditor[text]"] = input.content;
23
+ else if (type === "page")
24
+ values["page[text]"] = input.content;
25
+ else
26
+ throw new Error(`content is for pages and labels; for a ${type}, use description or settings.`);
27
+ }
28
+ if (input.url !== undefined)
29
+ values.externalurl = input.url;
30
+ if (input.visibility !== undefined)
31
+ values.visible = FORM_VISIBILITY[input.visibility];
32
+ return { ...values, ...input.settings };
33
+ }
34
+ async function afterChange(context, siteUrl, courseId, changed, extra = {}) {
35
+ return { changed, ...extra, sections: structureOf(await courseState(context, siteUrl, courseId)) };
36
+ }
37
+ /**
38
+ * Creates an activity or resource of any type at the end of a section, through Moodle's own form,
39
+ * hidden from students unless `visibility` says otherwise. `files` are local paths uploaded to a file
40
+ * resource or folder.
41
+ */
42
+ export async function createActivity(context, siteUrl, input) {
43
+ if (input.files)
44
+ await checkLocalFiles(input.files);
45
+ const before = await courseState(context, siteUrl, input.courseId);
46
+ const target = before.section.find((s) => Number(s.id) === input.sectionId);
47
+ if (!target)
48
+ throw new Error(`Course ${input.courseId} has no section ${input.sectionId}. Take its id from the course structure.`);
49
+ const page = await openForm(context, siteUrl, `course/modedit.php?add=${input.type}&course=${input.courseId}&section=${target.number}&return=0`);
50
+ try {
51
+ if (input.files && input.files.length > 0) {
52
+ const draft = await formValue(page, "files");
53
+ if (!draft)
54
+ throw new Error(`A ${input.type} takes no files in its form.`);
55
+ await uploadToDraft(page, draft, input.files);
56
+ }
57
+ // Hidden unless asked: a teacher publishes after checking.
58
+ await setFields(page, fieldsFor(input.type, { ...input, visibility: input.visibility ?? "hidden" }));
59
+ await submitForm(page, "submitbutton2");
60
+ }
61
+ finally {
62
+ await page.close().catch(() => undefined);
63
+ }
64
+ const after = await courseState(context, siteUrl, input.courseId);
65
+ const created = after.cm.find((cm) => !before.cm.some((old) => old.id === cm.id));
66
+ return {
67
+ changed: created ? `Created ${input.type} ${created.id} "${created.name}" in section ${target.number}.` : `The form was saved, but no new activity appears in course ${input.courseId}.`,
68
+ activityId: created ? Number(created.id) : undefined,
69
+ url: created?.url,
70
+ sections: structureOf(after),
71
+ };
72
+ }
73
+ /** Changes an existing activity through its form: only what's given changes; files stay as they are. */
74
+ export async function updateActivity(context, siteUrl, cmid, input) {
75
+ const page = await openForm(context, siteUrl, `course/modedit.php?update=${cmid}&return=0`);
76
+ let courseId;
77
+ try {
78
+ courseId = Number(await formValue(page, "course"));
79
+ const type = (await formValue(page, "modulename")) ?? "";
80
+ await setFields(page, fieldsFor(type, input));
81
+ await submitForm(page, "submitbutton2");
82
+ }
83
+ finally {
84
+ await page.close().catch(() => undefined);
85
+ }
86
+ const after = await courseState(context, siteUrl, courseId);
87
+ const cm = after.cm.find((c) => Number(c.id) === cmid);
88
+ return { changed: `Updated activity ${cmid}${cm ? ` "${cm.name}"` : ""}.`, activityId: cmid, url: cm?.url, sections: structureOf(after) };
89
+ }
90
+ /** Renames, shows or hides, or moves an activity (to a section's end, or before another one). */
91
+ export async function editActivity(context, siteUrl, courseId, cmid, { name, visibility, sectionId, beforeActivityId }) {
92
+ const done = [];
93
+ if (name !== undefined) {
94
+ await renameActivity(context, siteUrl, cmid, name);
95
+ done.push(`renamed to "${name}"`);
96
+ }
97
+ if (visibility !== undefined) {
98
+ await editCourse(context, siteUrl, courseId, VISIBILITY_ACTIONS[visibility], [cmid]);
99
+ done.push(`made ${visibility}`);
100
+ }
101
+ if (sectionId !== undefined || beforeActivityId !== undefined) {
102
+ await editCourse(context, siteUrl, courseId, "cm_move", [cmid], { sectionId, activityId: beforeActivityId });
103
+ done.push(beforeActivityId !== undefined ? `moved before activity ${beforeActivityId}` : `moved to the end of section ${sectionId}`);
104
+ }
105
+ return afterChange(context, siteUrl, courseId, done.length > 0 ? `Activity ${cmid} ${done.join(", ")}.` : "Nothing to change.");
106
+ }
107
+ export async function duplicateActivity(context, siteUrl, courseId, cmid) {
108
+ const before = new Set((await courseState(context, siteUrl, courseId)).cm.map((cm) => cm.id));
109
+ await editCourse(context, siteUrl, courseId, "cm_duplicate", [cmid]);
110
+ const copy = (await courseState(context, siteUrl, courseId)).cm.find((cm) => !before.has(cm.id));
111
+ return afterChange(context, siteUrl, courseId, `Activity ${cmid} duplicated${copy ? ` as ${copy.id}` : ""}.`, { newId: copy ? Number(copy.id) : undefined });
112
+ }
113
+ export async function deleteActivity(context, siteUrl, courseId, cmid) {
114
+ await editCourse(context, siteUrl, courseId, "cm_delete", [cmid]);
115
+ return afterChange(context, siteUrl, courseId, `Activity ${cmid} deleted.`);
116
+ }
117
+ /** Adds a section after another one (at the end without it), named when a name is given. */
118
+ export async function addSection(context, siteUrl, courseId, { name, afterSectionId } = {}) {
119
+ const before = new Set((await courseState(context, siteUrl, courseId)).section.map((s) => s.id));
120
+ await editCourse(context, siteUrl, courseId, "section_add", [], { sectionId: afterSectionId });
121
+ const added = (await courseState(context, siteUrl, courseId)).section.find((s) => !before.has(s.id));
122
+ if (added && name !== undefined)
123
+ await renameSection(context, siteUrl, courseId, Number(added.id), name);
124
+ return afterChange(context, siteUrl, courseId, `Section ${added?.id ?? "?"} added${name !== undefined ? ` as "${name}"` : ""}.`, { newId: added ? Number(added.id) : undefined });
125
+ }
126
+ /** Renames, shows or hides, or moves a section after another one. */
127
+ export async function editSection(context, siteUrl, courseId, sectionId, { name, visible, afterSectionId }) {
128
+ const done = [];
129
+ if (name !== undefined) {
130
+ await renameSection(context, siteUrl, courseId, sectionId, name);
131
+ done.push(`renamed to "${name}"`);
132
+ }
133
+ if (visible !== undefined) {
134
+ await editCourse(context, siteUrl, courseId, visible ? "section_show" : "section_hide", [sectionId]);
135
+ done.push(visible ? "shown" : "hidden");
136
+ }
137
+ if (afterSectionId !== undefined) {
138
+ await editCourse(context, siteUrl, courseId, "section_move_after", [sectionId], { sectionId: afterSectionId });
139
+ done.push(`moved after section ${afterSectionId}`);
140
+ }
141
+ return afterChange(context, siteUrl, courseId, done.length > 0 ? `Section ${sectionId} ${done.join(", ")}.` : "Nothing to change.");
142
+ }
143
+ /** Deletes a section and every activity in it, with their participants' data. */
144
+ export async function deleteSection(context, siteUrl, courseId, sectionId) {
145
+ await editCourse(context, siteUrl, courseId, "section_delete", [sectionId]);
146
+ return afterChange(context, siteUrl, courseId, `Section ${sectionId} deleted, with its activities.`);
147
+ }
148
+ /** The fields of a Moodle form in `page`, internal ones (sesskey, ids, buttons) left out, editors' HTML included. */
149
+ export function readFormFields(page) {
150
+ return inPage(page, () => {
151
+ const doc = globalThis.document;
152
+ const clean = (s) => (s ?? "").replace(/\s+/g, " ").trim();
153
+ // A field's label: its own <label for>, or its row's (a date selector's parts, a radio group).
154
+ const labelOf = (field) => {
155
+ const own = field.id ? doc.querySelector(`label[for="${field.id}"]`) : null;
156
+ const row = field.closest(".fitem")?.querySelector(".col-form-label label, .col-form-label p, legend");
157
+ return clean(own?.textContent) || clean(row?.textContent) || undefined;
158
+ };
159
+ const fields = [];
160
+ const internal = /^(sesskey|_qf__|mform_isexpanded_|mform_showmore_|course$|coursemodule$|section$|module$|modulename$|instance$|add$|update$|return$|sr$|beforemod$)/;
161
+ for (const field of Array.from(doc.querySelectorAll("form.mform [name]"))) {
162
+ const name = field.name;
163
+ if (!name || internal.test(name) || ["hidden", "submit", "button", "fieldset"].includes(field.type) || field.tagName === "FIELDSET")
164
+ continue;
165
+ if (field.type === "radio") {
166
+ let group = fields.find((f) => f.name === name);
167
+ if (!group) {
168
+ group = { name, type: "radio", label: clean(field.closest(".fitem")?.querySelector(".col-form-label, legend")?.textContent) || undefined, options: [] };
169
+ fields.push(group);
170
+ }
171
+ group.options.push({ value: field.value, label: clean((doc.querySelector(`label[for="${field.id}"]`) ?? field.closest("label"))?.textContent) || field.value });
172
+ if (field.checked)
173
+ group.value = field.value;
174
+ }
175
+ else if (field.type === "checkbox") {
176
+ fields.push({ name, type: "checkbox", label: labelOf(field), value: field.value, checked: field.checked });
177
+ }
178
+ else if (field.tagName === "SELECT") {
179
+ const options = Array.from(field.options);
180
+ const selected = options.filter((o) => o.selected).map((o) => o.value);
181
+ // A date selector's parts (duedate[day]…) are numbers, named by their row ("Due date (day)").
182
+ const part = /\[(day|month|year|hour|minute)\]$/.exec(name)?.[1];
183
+ if (part) {
184
+ // The group's label is id_<group>_label (id_duedate_label for duedate[day]).
185
+ const row = clean(doc.querySelector(`#id_${name.slice(0, name.indexOf("["))}_label`)?.textContent);
186
+ fields.push({ name, type: "date", label: row ? `${row} (${part})` : part, value: selected[0] });
187
+ }
188
+ else {
189
+ fields.push({ name, type: "select", label: labelOf(field), value: field.multiple ? selected : selected[0], options: options.map((o) => ({ value: o.value, label: clean(o.text) })) });
190
+ }
191
+ }
192
+ else {
193
+ const type = field.tagName === "TEXTAREA" ? "textarea" : field.type === "number" ? "number" : field.type === "text" || field.type === "url" || field.type === "email" ? "text" : "other";
194
+ fields.push({ name, type, label: labelOf(field), value: field.value });
195
+ }
196
+ }
197
+ // Moodle disables fields until another enables them (a date until its "enable", a maximum grade until rescaling is chosen).
198
+ for (const f of fields) {
199
+ const elements = Array.from(doc.querySelectorAll(`form.mform [name="${f.name}"]`)).filter((e) => e.type !== "hidden");
200
+ if (elements.length > 0 && elements.every((e) => e.disabled))
201
+ f.disabled = true;
202
+ // No label, no key: plain data for the program.
203
+ for (const key of Object.keys(f))
204
+ if (f[key] === undefined)
205
+ delete f[key];
206
+ }
207
+ return fields;
208
+ }, null);
209
+ }
210
+ /** Nobody can see that activity's settings: not an activity, or not the person's to edit. */
211
+ export class ActivitySettingsError extends Error {
212
+ name = "ActivitySettingsError";
213
+ }
214
+ /**
215
+ * An activity's settings as its form holds them, without submitting it: its module, name and
216
+ * description, and every field by the name `updateActivity` takes in `settings`. Teachers only.
217
+ */
218
+ export async function activitySettings(context, siteUrl, cmid) {
219
+ const page = await openSitePage(context, siteUrl, `course/modedit.php?update=${cmid}&return=0`);
220
+ try {
221
+ if ((await page.locator("form.mform").count()) === 0) {
222
+ throw new ActivitySettingsError(`You can't see activity ${cmid}'s settings: ${(await moodleErrorMessage(page)) ?? "it doesn't exist, or you can't edit it"}`);
223
+ }
224
+ await page.waitForLoadState("load");
225
+ const fields = await readFormFields(page);
226
+ const value = (name) => fields.find((f) => f.name === name)?.value;
227
+ return {
228
+ id: cmid,
229
+ module: (await formValue(page, "modulename")) ?? "",
230
+ courseId: Number(await formValue(page, "course")),
231
+ name: value("name") ?? "",
232
+ description: value("introeditor[text]") ?? "",
233
+ fields,
234
+ };
235
+ }
236
+ finally {
237
+ await page.close().catch(() => undefined);
238
+ }
239
+ }
240
+ //# sourceMappingURL=activities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.js","sourceRoot":"","sources":["../../src/moodle/activities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { BrowserContext } from "playwright-core";
2
+ /** A Moodle AJAX service answered with an exception. */
3
+ export declare class MoodleServiceError extends Error {
4
+ readonly methodname: string;
5
+ readonly errorcode: string;
6
+ name: string;
7
+ constructor(methodname: string, errorcode: string, message: string);
8
+ }
9
+ /** The response of `lib/ajax/service.php` for one request: `data`, or throws. */
10
+ export declare function parseServiceResponse<T>(methodname: string, status: number, body: string): T;
11
+ /**
12
+ * Calls one of Moodle's AJAX services as the logged-in person (ADR-004). Throws `NotLoggedInError`
13
+ * when there's no session, `MoodleServiceError` when the service fails.
14
+ */
15
+ export declare function callService<T>(context: BrowserContext, siteUrl: string, methodname: string, args: Record<string, unknown>): Promise<T>;
16
+ /** What `M.cfg` says about the logged-in person on the kept service tab. */
17
+ export interface PageConfig {
18
+ userId: number;
19
+ language: string;
20
+ timeZone: string;
21
+ }
22
+ export declare function readPageConfig(context: BrowserContext, siteUrl: string): Promise<PageConfig>;
@@ -0,0 +1,91 @@
1
+ import { NotLoggedInError, openSitePage } from "./session.js";
2
+ /** A Moodle AJAX service answered with an exception. */
3
+ export class MoodleServiceError extends Error {
4
+ methodname;
5
+ errorcode;
6
+ name = "MoodleServiceError";
7
+ constructor(methodname, errorcode, message) {
8
+ super(`Moodle's ${methodname} failed (${errorcode}): ${message}`);
9
+ this.methodname = methodname;
10
+ this.errorcode = errorcode;
11
+ }
12
+ }
13
+ // What service.php answers when the session is gone or the tab's sesskey is from an older one.
14
+ const SESSION_ERRORS = new Set(["servicerequireslogin", "invalidsesskey"]);
15
+ // A light page that needs a session: the tab only lends its cookies and `M.cfg.sesskey`.
16
+ const SERVICE_PAGE = "user/preferences.php";
17
+ /** The tab kept open per context for service calls (ADR-004), and the site it belongs to. */
18
+ const servicePages = new WeakMap();
19
+ /** The response of `lib/ajax/service.php` for one request: `data`, or throws. */
20
+ export function parseServiceResponse(methodname, status, body) {
21
+ let responses;
22
+ try {
23
+ responses = JSON.parse(body);
24
+ }
25
+ catch {
26
+ throw new MoodleServiceError(methodname, `http${status}`, "the answer isn't JSON");
27
+ }
28
+ const response = (Array.isArray(responses) ? responses[0] : responses);
29
+ if (!response)
30
+ throw new MoodleServiceError(methodname, `http${status}`, "empty answer");
31
+ if (response.error === false)
32
+ return response.data;
33
+ // A failure before any function runs (bad sesskey) comes as a bare exception object.
34
+ const exception = response.exception ?? response;
35
+ throw new MoodleServiceError(methodname, exception.errorcode ?? "unknown", exception.message ?? "no message");
36
+ }
37
+ async function postFromPage(page, methodname, args) {
38
+ return page.evaluate(async ({ methodname, args }) => {
39
+ // Runs in the page, with its cookies and Moodle's globals.
40
+ const g = globalThis;
41
+ const url = `${g.M.cfg.wwwroot}/lib/ajax/service.php?sesskey=${encodeURIComponent(g.M.cfg.sesskey)}&info=${encodeURIComponent(methodname)}`;
42
+ const response = await g.fetch(url, {
43
+ method: "POST",
44
+ credentials: "same-origin",
45
+ headers: { "Content-Type": "application/json" },
46
+ body: JSON.stringify([{ index: 0, methodname, args }]),
47
+ });
48
+ return { status: response.status, body: await response.text() };
49
+ }, { methodname, args });
50
+ }
51
+ async function servicePage(context, siteUrl, fresh) {
52
+ const current = servicePages.get(context);
53
+ // The program may have changed the site since the tab was opened.
54
+ if (current && !current.page.isClosed() && current.siteUrl === siteUrl && !fresh)
55
+ return current.page;
56
+ await current?.page.close().catch(() => undefined);
57
+ // The kept tab isn't a page a function works on: nothing to follow when it's replaced.
58
+ const page = await openSitePage(context, siteUrl, SERVICE_PAGE, { follow: false });
59
+ servicePages.set(context, { page, siteUrl });
60
+ return page;
61
+ }
62
+ /**
63
+ * Calls one of Moodle's AJAX services as the logged-in person (ADR-004). Throws `NotLoggedInError`
64
+ * when there's no session, `MoodleServiceError` when the service fails.
65
+ */
66
+ export async function callService(context, siteUrl, methodname, args) {
67
+ for (const fresh of [false, true]) {
68
+ const page = await servicePage(context, siteUrl, fresh);
69
+ const { status, body } = await postFromPage(page, methodname, args);
70
+ try {
71
+ return parseServiceResponse(methodname, status, body);
72
+ }
73
+ catch (error) {
74
+ const sessionError = error instanceof MoodleServiceError && SESSION_ERRORS.has(error.errorcode);
75
+ if (!sessionError)
76
+ throw error;
77
+ // The kept tab's session may be stale (a new login, an expiry): a new tab tells which.
78
+ if (fresh)
79
+ throw new NotLoggedInError();
80
+ }
81
+ }
82
+ throw new NotLoggedInError(); // Unreachable: the fresh attempt returns or throws.
83
+ }
84
+ export async function readPageConfig(context, siteUrl) {
85
+ const page = await servicePage(context, siteUrl, false);
86
+ return page.evaluate(() => {
87
+ const cfg = globalThis.M.cfg;
88
+ return { userId: cfg.userId, language: cfg.language, timeZone: cfg.usertimezone };
89
+ });
90
+ }
91
+ //# sourceMappingURL=ajax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ajax.js","sourceRoot":"","sources":["../../src/moodle/ajax.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE9D,wDAAwD;AACxD,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAGhC;IACA;IAHF,IAAI,GAAG,oBAAoB,CAAC;IACrC,YACW,UAAkB,EAClB,SAAiB,EAC1B,OAAe;QAEf,KAAK,CAAC,YAAY,UAAU,YAAY,SAAS,MAAM,OAAO,EAAE,CAAC,CAAC;QAJzD,eAAU,GAAV,UAAU,CAAQ;QAClB,cAAS,GAAT,SAAS,CAAQ;IAI5B,CAAC;CACF;AAED,+FAA+F;AAC/F,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,sBAAsB,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE3E,yFAAyF;AACzF,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAE5C,6FAA6F;AAC7F,MAAM,YAAY,GAAG,IAAI,OAAO,EAAmD,CAAC;AAEpF,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAI,UAAkB,EAAE,MAAc,EAAE,IAAY;IACtF,IAAI,SAAkB,CAAC;IACvB,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,OAAO,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAExD,CAAC;IACd,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,OAAO,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC;IACzF,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,QAAQ,CAAC,IAAS,CAAC;IACxD,qFAAqF;IACrF,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC;IACjD,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC,SAAS,IAAI,SAAS,EAAE,SAAS,CAAC,OAAO,IAAI,YAAY,CAAC,CAAC;AAChH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,UAAkB,EAAE,IAAa;IACvE,OAAO,IAAI,CAAC,QAAQ,CAClB,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE;QAC7B,2DAA2D;QAC3D,MAAM,CAAC,GAAG,UAGT,CAAC;QACF,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,iCAAiC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5I,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAClC,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,aAAa;YAC1B,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;SACvD,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IAClE,CAAC,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAuB,EAAE,OAAe,EAAE,KAAc;IACjF,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1C,kEAAkE;IAClE,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC;IACtG,MAAM,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnD,uFAAuF;IACvF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAI,OAAuB,EAAE,OAAe,EAAE,UAAkB,EAAE,IAA6B;IAC9H,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACxD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QACpE,IAAI,CAAC;YACH,OAAO,oBAAoB,CAAI,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,kBAAkB,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAChG,IAAI,CAAC,YAAY;gBAAE,MAAM,KAAK,CAAC;YAC/B,uFAAuF;YACvF,IAAI,KAAK;gBAAE,MAAM,IAAI,gBAAgB,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,MAAM,IAAI,gBAAgB,EAAE,CAAC,CAAC,oDAAoD;AACpF,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB,EAAE,OAAe;IAC3E,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;QACxB,MAAM,GAAG,GAAI,UAAoG,CAAC,CAAC,CAAC,GAAG,CAAC;QACxH,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,65 @@
1
+ import type { BrowserContext, Page } from "playwright-core";
2
+ import { type FieldValue } from "./forms.js";
3
+ /** An assignment's ids beyond its course module id, which only the grader page shows. */
4
+ export interface AssignmentIds {
5
+ instanceId: number;
6
+ contextId: number;
7
+ }
8
+ /** The id isn't an assignment's course module, or the person can't grade it. */
9
+ export declare class NotAnAssignmentError extends Error {
10
+ name: string;
11
+ }
12
+ /** Reads `data-assignmentid` and `data-contextid` from a page (the grader's), or `undefined`. */
13
+ export declare function readAssignmentIds(page: Page): Promise<AssignmentIds | undefined>;
14
+ export declare function assignmentIds(context: BrowserContext, siteUrl: string, cmid: number): Promise<AssignmentIds>;
15
+ /** A participant as `mod_assign_list_participants` returns it (Moodle 5.2), the fields read. */
16
+ export interface MoodleParticipant {
17
+ id: number;
18
+ fullname: string;
19
+ submitted: boolean;
20
+ requiregrading: boolean;
21
+ grantedextension: boolean;
22
+ submissionstatus: string;
23
+ }
24
+ /** The dates `mod_assign_get_participant` gives for one participant (Unix seconds; 0 is none). */
25
+ export interface MoodleParticipantDates {
26
+ allowsubmissionsfromdate: number;
27
+ duedate: number;
28
+ cutoffdate: number;
29
+ }
30
+ export declare function listParticipants(context: BrowserContext, siteUrl: string, instanceId: number): Promise<MoodleParticipant[]>;
31
+ export declare function participantDates(context: BrowserContext, siteUrl: string, instanceId: number, userId: number): Promise<MoodleParticipantDates>;
32
+ /** One submission plugin's content: its text, paragraphs kept, and its files. */
33
+ export interface PluginContent {
34
+ plugin: string;
35
+ text: string;
36
+ files: {
37
+ name: string;
38
+ url: string;
39
+ }[];
40
+ }
41
+ /**
42
+ * The content of a submission as the grader's panel renders it: per plugin, the full block
43
+ * (`.full_assignsubmission_<plugin>_<sid>`) or, for content short enough to have none, the summary
44
+ * one. Parsed in the page, by class only (ADR-004). The comments plugin isn't the submission.
45
+ */
46
+ export declare function readSubmissionContent(page: Page, html: string): Promise<PluginContent[]>;
47
+ /** The grader panel's HTML for one participant: their current submission and grade form. */
48
+ export declare function gradingPanelHtml(context: BrowserContext, siteUrl: string, contextId: number, userId: number): Promise<string>;
49
+ /** Moodle's grader didn't take the grade. */
50
+ export declare class GradingError extends Error {
51
+ name: string;
52
+ }
53
+ /**
54
+ * Fills the grading panel's form for one participant in `page` (a blank tab), and returns it
55
+ * serialized as the grader does (`form.serialize()`), ready for `mod_assign_submit_grading_form`.
56
+ */
57
+ export declare function fillGradingForm(page: Page, panelHtml: string, fields: Record<string, FieldValue>): Promise<string>;
58
+ /** The grade and feedback currently in a grading panel, if any. */
59
+ export declare function panelGrade(page: Page, panelHtml: string): Promise<{
60
+ grade?: string;
61
+ gradebookGrade?: string;
62
+ feedback?: string;
63
+ }>;
64
+ /** Submits a serialized grading form; Moodle answers warnings when it refuses it. */
65
+ export declare function submitGradingForm(context: BrowserContext, siteUrl: string, instanceId: number, userId: number, serialized: string): Promise<void>;