@falkenslab/moodle-driver 0.4.0 → 0.5.0
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.
- package/README.md +3 -2
- package/dist/areas/activities/build.d.ts +47 -10
- package/dist/areas/activities/build.js +157 -24
- package/dist/areas/activities/build.js.map +1 -1
- package/dist/areas/activities/conditions.d.ts +77 -0
- package/dist/areas/activities/conditions.js +216 -0
- package/dist/areas/activities/conditions.js.map +1 -0
- package/dist/areas/activities/content.d.ts +3 -0
- package/dist/areas/activities/content.js +1 -0
- package/dist/areas/activities/content.js.map +1 -1
- package/dist/areas/assignments/grader.d.ts +32 -0
- package/dist/areas/assignments/grader.js +66 -2
- package/dist/areas/assignments/grader.js.map +1 -1
- package/dist/areas/assignments/grading.d.ts +69 -6
- package/dist/areas/assignments/grading.js +173 -13
- package/dist/areas/assignments/grading.js.map +1 -1
- package/dist/areas/assignments/student.d.ts +14 -0
- package/dist/areas/assignments/student.js +43 -2
- package/dist/areas/assignments/student.js.map +1 -1
- package/dist/areas/backup/backup.d.ts +86 -0
- package/dist/areas/backup/backup.js +368 -0
- package/dist/areas/backup/backup.js.map +1 -0
- package/dist/areas/calendar/calendar.d.ts +27 -0
- package/dist/areas/calendar/calendar.js +115 -9
- package/dist/areas/calendar/calendar.js.map +1 -1
- package/dist/areas/completion/completion.d.ts +3 -0
- package/dist/areas/completion/completion.js.map +1 -1
- package/dist/areas/courses/list.d.ts +2 -0
- package/dist/areas/courses/list.js.map +1 -1
- package/dist/areas/courses/reset.d.ts +51 -0
- package/dist/areas/courses/reset.js +123 -0
- package/dist/areas/courses/reset.js.map +1 -0
- package/dist/areas/courses/state.d.ts +17 -0
- package/dist/areas/courses/state.js +37 -6
- package/dist/areas/courses/state.js.map +1 -1
- package/dist/areas/courses/structure.d.ts +14 -2
- package/dist/areas/courses/structure.js +11 -5
- package/dist/areas/courses/structure.js.map +1 -1
- package/dist/areas/forums/discussions.d.ts +8 -0
- package/dist/areas/forums/discussions.js +10 -0
- package/dist/areas/forums/discussions.js.map +1 -1
- package/dist/areas/forums/grading.d.ts +49 -0
- package/dist/areas/forums/grading.js +125 -0
- package/dist/areas/forums/grading.js.map +1 -0
- package/dist/areas/forums/posts.d.ts +37 -0
- package/dist/areas/forums/posts.js +141 -6
- package/dist/areas/forums/posts.js.map +1 -1
- package/dist/areas/grades/export.d.ts +38 -0
- package/dist/areas/grades/export.js +88 -0
- package/dist/areas/grades/export.js.map +1 -0
- package/dist/areas/grades/gradebook.d.ts +50 -0
- package/dist/areas/grades/gradebook.js +120 -0
- package/dist/areas/grades/gradebook.js.map +1 -1
- package/dist/areas/grades/singleview.d.ts +39 -0
- package/dist/areas/grades/singleview.js +186 -0
- package/dist/areas/grades/singleview.js.map +1 -0
- package/dist/areas/h5p/h5p.d.ts +2 -0
- package/dist/areas/h5p/h5p.js +12 -7
- package/dist/areas/h5p/h5p.js.map +1 -1
- package/dist/areas/lessons/lessons.d.ts +1 -0
- package/dist/areas/lessons/lessons.js.map +1 -1
- package/dist/areas/messages/messages.d.ts +36 -1
- package/dist/areas/messages/messages.js +60 -4
- package/dist/areas/messages/messages.js.map +1 -1
- package/dist/areas/participants/enrolments.d.ts +60 -0
- package/dist/areas/participants/enrolments.js +218 -0
- package/dist/areas/participants/enrolments.js.map +1 -0
- package/dist/areas/participants/groups.d.ts +75 -0
- package/dist/areas/participants/groups.js +201 -0
- package/dist/areas/participants/groups.js.map +1 -0
- package/dist/areas/participants/methods.d.ts +56 -0
- package/dist/areas/participants/methods.js +154 -0
- package/dist/areas/participants/methods.js.map +1 -0
- package/dist/areas/participants/participants.d.ts +11 -0
- package/dist/areas/participants/participants.js +24 -19
- package/dist/areas/participants/participants.js.map +1 -1
- package/dist/areas/questions/banks.d.ts +36 -1
- package/dist/areas/questions/banks.js +140 -17
- package/dist/areas/questions/banks.js.map +1 -1
- package/dist/areas/quizzes/attempts.d.ts +2 -0
- package/dist/areas/quizzes/attempts.js +9 -0
- package/dist/areas/quizzes/attempts.js.map +1 -1
- package/dist/areas/quizzes/grading.d.ts +50 -0
- package/dist/areas/quizzes/grading.js +131 -0
- package/dist/areas/quizzes/grading.js.map +1 -0
- package/dist/areas/quizzes/results.d.ts +3 -0
- package/dist/areas/quizzes/results.js +2 -1
- package/dist/areas/quizzes/results.js.map +1 -1
- package/dist/areas/quizzes/structure.d.ts +65 -9
- package/dist/areas/quizzes/structure.js +222 -23
- package/dist/areas/quizzes/structure.js.map +1 -1
- package/dist/areas/ratings/ratings.d.ts +26 -0
- package/dist/areas/ratings/ratings.js +108 -0
- package/dist/areas/ratings/ratings.js.map +1 -0
- package/dist/areas/reports/dates.d.ts +18 -0
- package/dist/areas/reports/dates.js +134 -0
- package/dist/areas/reports/dates.js.map +1 -0
- package/dist/areas/reports/reports.d.ts +143 -0
- package/dist/areas/reports/reports.js +267 -0
- package/dist/areas/reports/reports.js.map +1 -0
- package/dist/areas/scorm/scorm.d.ts +1 -0
- package/dist/areas/scorm/scorm.js.map +1 -1
- package/dist/areas/site/users.d.ts +7 -1
- package/dist/areas/site/users.js +9 -2
- package/dist/areas/site/users.js.map +1 -1
- package/dist/areas/surveys/choice.d.ts +2 -0
- package/dist/areas/surveys/choice.js +6 -3
- package/dist/areas/surveys/choice.js.map +1 -1
- package/dist/areas/surveys/surveys.d.ts +3 -0
- package/dist/areas/surveys/surveys.js +1 -0
- package/dist/areas/surveys/surveys.js.map +1 -1
- package/dist/areas/workshops/workshops.d.ts +133 -0
- package/dist/areas/workshops/workshops.js +335 -0
- package/dist/areas/workshops/workshops.js.map +1 -0
- package/dist/browser/background.d.ts +43 -0
- package/dist/browser/background.js +164 -0
- package/dist/browser/background.js.map +1 -0
- package/dist/browser/host.d.ts +30 -0
- package/dist/browser/host.js +373 -0
- package/dist/browser/host.js.map +1 -0
- package/dist/browser/manager.d.ts +9 -0
- package/dist/browser/manager.js +8 -0
- package/dist/browser/manager.js.map +1 -1
- package/dist/categories.d.ts +1 -0
- package/dist/categories.js +67 -0
- package/dist/categories.js.map +1 -1
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.js +49 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/background.d.ts +23 -0
- package/dist/cli/background.js +128 -0
- package/dist/cli/background.js.map +1 -0
- package/dist/cli/command.d.ts +66 -0
- package/dist/cli/command.js +328 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/commands.generated.js +2636 -578
- package/dist/cli/commands.generated.js.map +1 -1
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +2 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/main.js +30 -10
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.d.ts +3 -14
- package/dist/cli/run.js +9 -185
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +9 -0
- package/dist/cli/shell.js +233 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/client.d.ts +71 -8
- package/dist/client.js +75 -8
- package/dist/client.js.map +1 -1
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/files.d.ts +5 -0
- package/dist/core/files.js +7 -3
- package/dist/core/files.js.map +1 -1
- package/dist/core/forms.d.ts +33 -3
- package/dist/core/forms.js +105 -8
- package/dist/core/forms.js.map +1 -1
- package/dist/core/http.d.ts +7 -0
- package/dist/core/http.js +28 -7
- package/dist/core/http.js.map +1 -1
- package/dist/core/login.d.ts +3 -0
- package/dist/core/login.js +2 -0
- package/dist/core/login.js.map +1 -1
- package/dist/core/session.d.ts +12 -1
- package/dist/core/session.js +12 -1
- package/dist/core/session.js.map +1 -1
- package/dist/index.d.ts +35 -11
- package/dist/index.js +32 -12
- package/dist/index.js.map +1 -1
- package/dist/internals.d.ts +9 -1
- package/dist/internals.js +9 -4
- package/dist/internals.js.map +1 -1
- package/dist/options.d.ts +2 -0
- package/dist/options.js.map +1 -1
- package/dist/testing/fakeMoodle.d.ts +9 -2
- package/dist/testing/fakeMoodle.js +5 -3
- package/dist/testing/fakeMoodle.js.map +1 -1
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { callService } from "../../core/ajax.js";
|
|
2
|
+
import { openSitePage } from "../../core/session.js";
|
|
3
|
+
import { MoodleError } from "../../core/errors.js";
|
|
4
|
+
import { GradingError } from "../assignments/grader.js";
|
|
5
|
+
import { NotAForumError } from "./discussions.js";
|
|
6
|
+
async function forumGrading(context, siteUrl, activityId) {
|
|
7
|
+
const page = await openSitePage(context, siteUrl, `mod/forum/view.php?id=${activityId}`);
|
|
8
|
+
try {
|
|
9
|
+
if ((await page.locator("body#page-mod-forum-view").count()) === 0) {
|
|
10
|
+
throw new NotAForumError(`Activity ${activityId} isn't a forum you can view. Take the id of a forum (module "forum") from the course structure.`);
|
|
11
|
+
}
|
|
12
|
+
const button = await page.evaluate(() => {
|
|
13
|
+
const node = globalThis.document.querySelector('[data-grade-action="launch"][data-gradable-itemtype="forum"]');
|
|
14
|
+
if (!node)
|
|
15
|
+
return undefined;
|
|
16
|
+
return {
|
|
17
|
+
contextId: Number(node.getAttribute("data-contextid")),
|
|
18
|
+
courseId: Number(node.getAttribute("data-course-id")),
|
|
19
|
+
component: node.getAttribute("data-grading-component") ?? "",
|
|
20
|
+
method: node.getAttribute("data-grading-component-subtype") ?? "",
|
|
21
|
+
onlyActive: node.getAttribute("data-grade-only-active-users") === "1",
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
if (!button) {
|
|
25
|
+
throw new GradingError(`Forum ${activityId} isn't graded as a whole (its "Whole forum grading" is off), or you can't grade it.`);
|
|
26
|
+
}
|
|
27
|
+
return button;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await page.close().catch(() => undefined);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function directMethod(activityId, grading) {
|
|
34
|
+
if (grading.component === "core_grades" && (grading.method === "point" || grading.method === "scale"))
|
|
35
|
+
return grading.method;
|
|
36
|
+
throw new GradingError(`Forum ${activityId} is graded with an advanced grading method (${grading.component}): only a grade out of points or on a scale can be given here.`);
|
|
37
|
+
}
|
|
38
|
+
function toGrade(user, method, panel) {
|
|
39
|
+
// A scale's "No grade" has no positive value; a point grade of 0 is a grade.
|
|
40
|
+
const value = method === "scale" ? panel.grade.options?.find((o) => o.selected && o.value > 0)?.value : panel.grade.grade;
|
|
41
|
+
return {
|
|
42
|
+
userId: user.id,
|
|
43
|
+
fullName: user.fullname,
|
|
44
|
+
...(panel.hasgrade && panel.grade.usergrade !== "-" && panel.grade.usergrade !== "" ? { grade: panel.grade.usergrade } : {}),
|
|
45
|
+
...(panel.hasgrade && typeof value === "number" && value >= 0 ? { value } : {}),
|
|
46
|
+
...(panel.hasgrade && panel.grade.gradedby ? { gradedBy: panel.grade.gradedby } : {}),
|
|
47
|
+
...(panel.hasgrade && panel.grade.timemodified ? { gradedAt: new Date(panel.grade.timemodified * 1000).toISOString() } : {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function fetchPanel(context, siteUrl, method, contextId, userId) {
|
|
51
|
+
return callService(context, siteUrl, `core_grades_grader_gradingpanel_${method}_fetch`, { component: "mod_forum", contextid: contextId, itemname: "forum", gradeduserid: userId });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Everyone's whole-forum grade in a forum graded as a whole, as its grader shows them: one per
|
|
55
|
+
* person it can grade (only the active ones when the forum says so), with how it's graded. One
|
|
56
|
+
* service call per person. Throws `NotAForumError` for another activity, and `GradingError` when
|
|
57
|
+
* the forum isn't graded as a whole or uses an advanced grading method.
|
|
58
|
+
*/
|
|
59
|
+
export async function forumGrades(context, siteUrl, activityId, { groupId } = {}) {
|
|
60
|
+
const grading = await forumGrading(context, siteUrl, activityId);
|
|
61
|
+
const method = directMethod(activityId, grading);
|
|
62
|
+
const { users } = await callService(context, siteUrl, "core_grades_get_gradable_users", {
|
|
63
|
+
courseid: grading.courseId,
|
|
64
|
+
groupid: groupId ?? 0,
|
|
65
|
+
onlyactive: grading.onlyActive,
|
|
66
|
+
});
|
|
67
|
+
const result = { activityId, method, grades: [] };
|
|
68
|
+
for (const user of users) {
|
|
69
|
+
const panel = await fetchPanel(context, siteUrl, method, grading.contextId, user.id);
|
|
70
|
+
if (method === "point" && result.maxGrade === undefined && Number(panel.grade.maxgrade))
|
|
71
|
+
result.maxGrade = Number(panel.grade.maxgrade);
|
|
72
|
+
if (method === "scale" && !result.scale && panel.grade.options)
|
|
73
|
+
result.scale = panel.grade.options.filter((o) => o.value > 0).map((o) => ({ value: o.value, name: o.title }));
|
|
74
|
+
result.grades.push(toGrade(user, method, panel));
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Gives a person their whole-forum grade, as the forum's grader does: points (out of the forum's
|
|
80
|
+
* maximum), or a scale item by name or value; `null` takes the grade away. Moodle's forum grader
|
|
81
|
+
* has no feedback text. `notify` sends the person Moodle's notification, as the grader's checkbox
|
|
82
|
+
* does. Returns a sentence of what changed and the grade afterwards. Throws `GradingError` when the
|
|
83
|
+
* forum isn't graded as a whole, the grade is out of range or not on the scale, or Moodle refuses it.
|
|
84
|
+
*/
|
|
85
|
+
export async function gradeForumUser(context, siteUrl, activityId, userId, { grade, notify = false }) {
|
|
86
|
+
const grading = await forumGrading(context, siteUrl, activityId);
|
|
87
|
+
const method = directMethod(activityId, grading);
|
|
88
|
+
const before = await fetchPanel(context, siteUrl, method, grading.contextId, userId);
|
|
89
|
+
let value;
|
|
90
|
+
// A scale's "No grade" is -1, as in the panel's menu.
|
|
91
|
+
if (grade === null)
|
|
92
|
+
value = -1;
|
|
93
|
+
else if (method === "scale") {
|
|
94
|
+
const options = (before.grade.options ?? []).filter((o) => o.value > 0);
|
|
95
|
+
const option = options.find((o) => o.value === Number(grade) || o.title === String(grade));
|
|
96
|
+
if (!option)
|
|
97
|
+
throw new GradingError(`Forum ${activityId} is graded on a scale; give one of its items: ${options.map((o) => `"${o.title}"`).join(", ")}.`);
|
|
98
|
+
value = option.value;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
value = Number(grade);
|
|
102
|
+
const max = Number(before.grade.maxgrade);
|
|
103
|
+
if (!Number.isFinite(value) || value < 0 || (max > 0 && value > max))
|
|
104
|
+
throw new GradingError(`Forum ${activityId} takes grades from 0 to ${max || "its maximum"}, not ${grade}.`);
|
|
105
|
+
}
|
|
106
|
+
if (!Number.isFinite(value))
|
|
107
|
+
throw new MoodleError(`The grade must be a number: ${grade}.`, "invalid-input");
|
|
108
|
+
// An emptied point grade is sent empty, as the panel's emptied field is.
|
|
109
|
+
const formdata = new URLSearchParams({ grade: grade === null && method === "point" ? "" : String(value) }).toString();
|
|
110
|
+
const after = await callService(context, siteUrl, `core_grades_grader_gradingpanel_${method}_store`, {
|
|
111
|
+
component: "mod_forum",
|
|
112
|
+
contextid: grading.contextId,
|
|
113
|
+
itemname: "forum",
|
|
114
|
+
gradeduserid: userId,
|
|
115
|
+
notifyuser: notify,
|
|
116
|
+
formdata,
|
|
117
|
+
});
|
|
118
|
+
const result = toGrade({ id: userId, fullname: "" }, method, after);
|
|
119
|
+
delete result.fullName;
|
|
120
|
+
return {
|
|
121
|
+
changed: grade === null ? `User ${userId}'s grade in forum ${activityId} taken away.` : `User ${userId} graded ${result.grade ?? grade} in forum ${activityId}${notify ? ", notified" : ""}.`,
|
|
122
|
+
grade: result,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=grading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grading.js","sourceRoot":"","sources":["../../../src/areas/forums/grading.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { BrowserContext } from "playwright-core";
|
|
2
2
|
import { type FieldValue } from "../../core/forms.js";
|
|
3
|
+
/** A discussion of a forum, as `forumDiscussions` lists it. */
|
|
3
4
|
export interface ForumDiscussion {
|
|
4
5
|
id: number;
|
|
5
6
|
subject: string;
|
|
7
|
+
/** Who started it, by name. */
|
|
6
8
|
author: string;
|
|
7
9
|
started?: string;
|
|
8
10
|
lastPost: {
|
|
@@ -16,8 +18,10 @@ export interface ForumDiscussion {
|
|
|
16
18
|
}
|
|
17
19
|
/** A forum's discussions, as its page lists them: who started each, replies, last post. */
|
|
18
20
|
export declare function forumDiscussions(context: BrowserContext, siteUrl: string, activityId: number): Promise<ForumDiscussion[]>;
|
|
21
|
+
/** A post of a discussion, as `discussion` reads it. */
|
|
19
22
|
export interface ForumPost {
|
|
20
23
|
id: number;
|
|
24
|
+
/** The post it replies to; none for the discussion's first. */
|
|
21
25
|
parentId?: number;
|
|
22
26
|
subject: string;
|
|
23
27
|
author: {
|
|
@@ -45,6 +49,7 @@ export declare function discussion(context: BrowserContext, siteUrl: string, dis
|
|
|
45
49
|
};
|
|
46
50
|
posts: ForumPost[];
|
|
47
51
|
}>;
|
|
52
|
+
/** A post to write, for `startDiscussion` and `replyToPost`. */
|
|
48
53
|
export interface PostInput {
|
|
49
54
|
/** The message (HTML). */
|
|
50
55
|
message: string;
|
|
@@ -81,3 +86,35 @@ export declare function setDiscussionState(context: BrowserContext, siteUrl: str
|
|
|
81
86
|
changed: string;
|
|
82
87
|
discussion?: ForumDiscussion;
|
|
83
88
|
}>;
|
|
89
|
+
/** What to change in a post: only what's given changes. */
|
|
90
|
+
export interface PostChanges {
|
|
91
|
+
subject?: string;
|
|
92
|
+
/** The new message (HTML), replacing the old one. */
|
|
93
|
+
message?: string;
|
|
94
|
+
/** Attachments to add (absolute local paths) and to remove (by name, as the post lists them). */
|
|
95
|
+
files?: {
|
|
96
|
+
add?: string[];
|
|
97
|
+
remove?: string[];
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Changes a post through Moodle's own post form (`post.php?edit=`), as its author (within the site's
|
|
102
|
+
* editing time) or a moderator: only what's given changes. Returns the post afterwards. Throws
|
|
103
|
+
* `ForumError` with Moodle's reason when it refuses.
|
|
104
|
+
*/
|
|
105
|
+
export declare function editPost(context: BrowserContext, siteUrl: string, postId: number, changes: PostChanges): Promise<{
|
|
106
|
+
changed: string;
|
|
107
|
+
post?: ForumPost;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* Deletes a post and its replies through Moodle's own confirmation (`post.php?delete=`); a
|
|
111
|
+
* discussion's first post takes the whole discussion. Only a moderator deletes a post with replies,
|
|
112
|
+
* or someone else's. `replies` says how many replies went with it. Throws `ForumError` with
|
|
113
|
+
* Moodle's reason when it refuses.
|
|
114
|
+
*/
|
|
115
|
+
export declare function deletePost(context: BrowserContext, siteUrl: string, postId: number): Promise<{
|
|
116
|
+
changed: string;
|
|
117
|
+
discussionId?: number;
|
|
118
|
+
replies: number;
|
|
119
|
+
discussionDeleted: boolean;
|
|
120
|
+
}>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { callService } from "../../core/ajax.js";
|
|
2
2
|
import { isoDate } from "../courses/list.js";
|
|
3
3
|
import { checkLocalFiles } from "../../core/files.js";
|
|
4
|
-
import { formValue, openForm, setFields, submitForm, uploadToDraft } from "../../core/forms.js";
|
|
5
|
-
import { NotAForumError, discussionPosts, forumInstanceId, htmlToTexts, listDiscussions } from "./discussions.js";
|
|
4
|
+
import { FormError, formValue, openForm, removeFromDraft, setFields, submitForm, uploadToDraft } from "../../core/forms.js";
|
|
5
|
+
import { ForumError, NotAForumError, discussionPosts, forumInstanceId, htmlToTexts, listDiscussions } from "./discussions.js";
|
|
6
|
+
import { fetchSitePage, moodleErrorMessage, openSitePage } from "../../core/session.js";
|
|
6
7
|
import { decodeEntities } from "../../core/text.js";
|
|
7
8
|
import { MoodleError } from "../../core/errors.js";
|
|
8
9
|
/** A forum's discussions, as its page lists them: who started each, replies, last post. */
|
|
@@ -39,12 +40,16 @@ export async function discussion(context, siteUrl, discussionId) {
|
|
|
39
40
|
})),
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
/**
|
|
43
|
-
|
|
43
|
+
/** Refuses a message with nothing in it before any form is opened. */
|
|
44
|
+
function checkMessage(message) {
|
|
44
45
|
// Moodle takes "<p></p>" as a message and posts it blank (confirmed empirically, 5.2).
|
|
45
|
-
const text =
|
|
46
|
-
if (text === "" && !/<(img|video|audio|iframe)\b/i.test(
|
|
46
|
+
const text = message.replace(/<[^>]*>/g, "").replace(/ /g, " ").trim();
|
|
47
|
+
if (text === "" && !/<(img|video|audio|iframe)\b/i.test(message))
|
|
47
48
|
throw new MoodleError("The message is empty: write something before posting.", "invalid-input");
|
|
49
|
+
}
|
|
50
|
+
/** Fills and submits the post form at `formPath`; returns the URL Moodle lands on. */
|
|
51
|
+
async function post(context, siteUrl, formPath, input) {
|
|
52
|
+
checkMessage(input.message);
|
|
48
53
|
if (input.files)
|
|
49
54
|
await checkLocalFiles(input.files);
|
|
50
55
|
const page = await openForm(context, siteUrl, formPath);
|
|
@@ -111,4 +116,134 @@ export async function setDiscussionState(context, siteUrl, activityId, discussio
|
|
|
111
116
|
}
|
|
112
117
|
return { changed: done.length > 0 ? `Discussion ${discussionId} ${done.join(" and ")}.` : "Nothing to change.", discussion: await find() };
|
|
113
118
|
}
|
|
119
|
+
/** Moodle's reason on a notification it redirected with (`#user-notifications .alert-danger`), or undefined. */
|
|
120
|
+
function refusalNotice(page) {
|
|
121
|
+
return page.evaluate(() => {
|
|
122
|
+
const doc = globalThis.document;
|
|
123
|
+
const box = doc.querySelector("#user-notifications .alert-danger");
|
|
124
|
+
if (!box)
|
|
125
|
+
return undefined;
|
|
126
|
+
const copy = box.cloneNode(true);
|
|
127
|
+
for (const extra of Array.from(copy.querySelectorAll("button, .visually-hidden")))
|
|
128
|
+
extra.remove();
|
|
129
|
+
return (copy.textContent ?? "").replace(/\s+/g, " ").trim() || undefined;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Moodle's message on the error page `path` answers with (its fatal error box), read over HTTP. */
|
|
133
|
+
async function errorPageMessage(context, siteUrl, path) {
|
|
134
|
+
const { html } = await fetchSitePage(context, siteUrl, path, { follow: false });
|
|
135
|
+
const message = /data-rel="fatalerror"[^>]*>\s*<p class="errormessage">([\s\S]*?)<\/p>/.exec(html)?.[1];
|
|
136
|
+
return message === undefined ? undefined : decodeEntities(message.replace(/<[^>]*>/g, "")).replace(/\s+/g, " ").trim();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Changes a post through Moodle's own post form (`post.php?edit=`), as its author (within the site's
|
|
140
|
+
* editing time) or a moderator: only what's given changes. Returns the post afterwards. Throws
|
|
141
|
+
* `ForumError` with Moodle's reason when it refuses.
|
|
142
|
+
*/
|
|
143
|
+
export async function editPost(context, siteUrl, postId, changes) {
|
|
144
|
+
const add = changes.files?.add ?? [];
|
|
145
|
+
const remove = changes.files?.remove ?? [];
|
|
146
|
+
if (changes.message !== undefined)
|
|
147
|
+
checkMessage(changes.message);
|
|
148
|
+
if (changes.subject !== undefined && changes.subject.trim() === "")
|
|
149
|
+
throw new MoodleError("The subject is empty: a post needs one.", "invalid-input");
|
|
150
|
+
if (changes.subject === undefined && changes.message === undefined && add.length === 0 && remove.length === 0)
|
|
151
|
+
return { changed: "Nothing to change." };
|
|
152
|
+
if (add.length > 0)
|
|
153
|
+
await checkLocalFiles(add);
|
|
154
|
+
const formPath = `mod/forum/post.php?edit=${postId}`;
|
|
155
|
+
let page;
|
|
156
|
+
try {
|
|
157
|
+
page = await openForm(context, siteUrl, formPath);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// Moodle refuses with an error page, not a form: edit time over, someone else's post, no such post.
|
|
161
|
+
if (!(error instanceof FormError))
|
|
162
|
+
throw error;
|
|
163
|
+
throw new ForumError(`Post ${postId} can't be edited: ${(await errorPageMessage(context, siteUrl, formPath)) ?? "Moodle didn't show its form"}`);
|
|
164
|
+
}
|
|
165
|
+
let discussionId;
|
|
166
|
+
try {
|
|
167
|
+
discussionId = Number(await formValue(page, "discussion"));
|
|
168
|
+
if (add.length > 0 || remove.length > 0) {
|
|
169
|
+
const draft = await formValue(page, "attachments");
|
|
170
|
+
if (!draft)
|
|
171
|
+
throw new MoodleError("This forum takes no attachments.", "not-available");
|
|
172
|
+
if (remove.length > 0)
|
|
173
|
+
await removeFromDraft(page, draft, remove);
|
|
174
|
+
if (add.length > 0)
|
|
175
|
+
await uploadToDraft(page, draft, add);
|
|
176
|
+
}
|
|
177
|
+
const values = {};
|
|
178
|
+
if (changes.subject !== undefined)
|
|
179
|
+
values.subject = changes.subject;
|
|
180
|
+
if (changes.message !== undefined)
|
|
181
|
+
values["message[text]"] = changes.message;
|
|
182
|
+
await setFields(page, values);
|
|
183
|
+
await submitForm(page, "submitbutton");
|
|
184
|
+
// A refusal after posting comes back as a notification on the discussion.
|
|
185
|
+
const refused = await refusalNotice(page);
|
|
186
|
+
if (refused)
|
|
187
|
+
throw new ForumError(`Post ${postId} wasn't changed: ${refused}`);
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
await page.close().catch(() => undefined);
|
|
191
|
+
}
|
|
192
|
+
const done = [
|
|
193
|
+
changes.subject !== undefined && "subject",
|
|
194
|
+
changes.message !== undefined && "message",
|
|
195
|
+
add.length > 0 && `${add.length} attachment(s) added`,
|
|
196
|
+
remove.length > 0 && `${remove.length} attachment(s) removed`,
|
|
197
|
+
].filter(Boolean);
|
|
198
|
+
const post = discussionId ? (await discussion(context, siteUrl, discussionId)).posts.find((p) => p.id === postId) : undefined;
|
|
199
|
+
return { changed: `Edited post ${postId}: ${done.join(", ")}.`, post };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Deletes a post and its replies through Moodle's own confirmation (`post.php?delete=`); a
|
|
203
|
+
* discussion's first post takes the whole discussion. Only a moderator deletes a post with replies,
|
|
204
|
+
* or someone else's. `replies` says how many replies went with it. Throws `ForumError` with
|
|
205
|
+
* Moodle's reason when it refuses.
|
|
206
|
+
*/
|
|
207
|
+
export async function deletePost(context, siteUrl, postId) {
|
|
208
|
+
const page = await openSitePage(context, siteUrl, `mod/forum/post.php?delete=${postId}`);
|
|
209
|
+
try {
|
|
210
|
+
const fatal = await moodleErrorMessage(page);
|
|
211
|
+
if (fatal)
|
|
212
|
+
throw new ForumError(`Post ${postId} can't be deleted: ${fatal}`);
|
|
213
|
+
// Moodle asks to confirm with a form posting `confirm`; a refusal redirects to the discussion with a notification.
|
|
214
|
+
const confirm = page.locator('form[action$="post.php"]:has(input[name="confirm"]) [type="submit"]');
|
|
215
|
+
if ((await confirm.count()) === 0)
|
|
216
|
+
throw new ForumError(`Post ${postId} can't be deleted: ${(await refusalNotice(page)) ?? "Moodle didn't ask to confirm it"}`);
|
|
217
|
+
// The cancel button goes back to the discussion.
|
|
218
|
+
const discussionId = Number(await page.locator('form[action*="discuss.php"] input[name="d"]').first().getAttribute("value").catch(() => null)) || undefined;
|
|
219
|
+
let replies = 0;
|
|
220
|
+
let first = false;
|
|
221
|
+
if (discussionId) {
|
|
222
|
+
const { posts } = await discussionPosts(context, siteUrl, discussionId);
|
|
223
|
+
first = posts.find((p) => p.id === postId)?.parentid === null;
|
|
224
|
+
// Every post under it, at any depth.
|
|
225
|
+
const under = new Set([postId]);
|
|
226
|
+
for (let grew = true; grew;) {
|
|
227
|
+
grew = false;
|
|
228
|
+
for (const p of posts) {
|
|
229
|
+
if (p.parentid !== null && under.has(p.parentid) && !under.has(p.id)) {
|
|
230
|
+
under.add(p.id);
|
|
231
|
+
grew = true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
replies = under.size - 1;
|
|
236
|
+
}
|
|
237
|
+
await Promise.all([page.waitForNavigation({ waitUntil: "domcontentloaded" }), confirm.first().click({ noWaitAfter: true })]);
|
|
238
|
+
const refused = await refusalNotice(page);
|
|
239
|
+
if (refused)
|
|
240
|
+
throw new ForumError(`Post ${postId} wasn't deleted: ${refused}`);
|
|
241
|
+
const what = first ? `discussion ${discussionId} (its first post ${postId})` : `post ${postId}`;
|
|
242
|
+
const withReplies = replies === 0 ? "" : ` and ${replies} ${replies === 1 ? "reply" : "replies"} under it`;
|
|
243
|
+
return { changed: `Deleted ${what}${withReplies}.`, discussionId, replies, discussionDeleted: first };
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
await page.close().catch(() => undefined);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
114
249
|
//# sourceMappingURL=posts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posts.js","sourceRoot":"","sources":["../../../src/areas/forums/posts.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"posts.js","sourceRoot":"","sources":["../../../src/areas/forums/posts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { BrowserContext } from "playwright-core";
|
|
2
|
+
import { type DownloadedFile } from "../../core/files.js";
|
|
3
|
+
/** The export formats by the export plugin that makes each (Moodle's "Excel spreadsheet" is an .xlsx). */
|
|
4
|
+
declare const PLUGINS: {
|
|
5
|
+
readonly xlsx: "xls";
|
|
6
|
+
readonly ods: "ods";
|
|
7
|
+
readonly csv: "txt";
|
|
8
|
+
readonly xml: "xml";
|
|
9
|
+
};
|
|
10
|
+
/** A file format `exportGrades` saves grades in (one of `GRADE_EXPORT_FORMATS`). */
|
|
11
|
+
export type GradeExportFormat = keyof typeof PLUGINS;
|
|
12
|
+
/** The formats grades can be exported in, where the site has their export plugin enabled. */
|
|
13
|
+
export declare const GRADE_EXPORT_FORMATS: GradeExportFormat[];
|
|
14
|
+
/** How grades are written: the points, the percentage, the letter (several for every format but XML). */
|
|
15
|
+
export type GradeExportDisplay = "real" | "percentage" | "letter";
|
|
16
|
+
/** What `exportGrades` exports and where it saves it, as the export form chooses. */
|
|
17
|
+
export interface ExportGradesOptions {
|
|
18
|
+
format: GradeExportFormat;
|
|
19
|
+
/** The grade items to export (ids from `graderReport` or `gradebookSetup`); all of them when left out. */
|
|
20
|
+
itemIds?: number[];
|
|
21
|
+
/** Only this group's members (a group id from `groups`). */
|
|
22
|
+
groupId?: number;
|
|
23
|
+
/** How grades are written; the site's default when left out. XML takes only one. */
|
|
24
|
+
display?: GradeExportDisplay[];
|
|
25
|
+
/** Include each grade's feedback. */
|
|
26
|
+
feedback?: boolean;
|
|
27
|
+
/** The folder to save the file in (an absolute path); the `Moodle` object's `downloadDir` when left out there. */
|
|
28
|
+
destDir?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Saves a course's grades into `destDir` as Moodle exports them for a teacher: a spreadsheet
|
|
32
|
+
* (`xlsx`, `ods`), comma-separated text (`csv`) or XML, with the items, group, display and feedback
|
|
33
|
+
* chosen as in the export form. Returns the file as `downloadFile` does (never overwriting one).
|
|
34
|
+
* Throws `GradebookError` when the site has that format's export plugin disabled (naming the ones
|
|
35
|
+
* it has), when an item isn't in the course, or when Moodle refuses the export.
|
|
36
|
+
*/
|
|
37
|
+
export declare function exportGrades(context: BrowserContext, siteUrl: string, courseId: number, options: ExportGradesOptions): Promise<DownloadedFile>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// A course's grades as Moodle exports them: the export plugin's form (grade/export/<plugin>/index.php)
|
|
2
|
+
// filled and sent as its button does, and the file it answers with saved as a download.
|
|
3
|
+
import { mkdir, stat } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { setFields } from "../../core/forms.js";
|
|
6
|
+
import { fileNameFor, freePath } from "../../core/files.js";
|
|
7
|
+
import { moodleErrorMessage, openSitePage } from "../../core/session.js";
|
|
8
|
+
import { MoodleError } from "../../core/errors.js";
|
|
9
|
+
import { GradebookError } from "./gradebook.js";
|
|
10
|
+
/** The export formats by the export plugin that makes each (Moodle's "Excel spreadsheet" is an .xlsx). */
|
|
11
|
+
const PLUGINS = { xlsx: "xls", ods: "ods", csv: "txt", xml: "xml" };
|
|
12
|
+
/** The formats grades can be exported in, where the site has their export plugin enabled. */
|
|
13
|
+
export const GRADE_EXPORT_FORMATS = Object.keys(PLUGINS);
|
|
14
|
+
const DISPLAY_CODES = { real: 1, percentage: 2, letter: 3 };
|
|
15
|
+
/**
|
|
16
|
+
* Saves a course's grades into `destDir` as Moodle exports them for a teacher: a spreadsheet
|
|
17
|
+
* (`xlsx`, `ods`), comma-separated text (`csv`) or XML, with the items, group, display and feedback
|
|
18
|
+
* chosen as in the export form. Returns the file as `downloadFile` does (never overwriting one).
|
|
19
|
+
* Throws `GradebookError` when the site has that format's export plugin disabled (naming the ones
|
|
20
|
+
* it has), when an item isn't in the course, or when Moodle refuses the export.
|
|
21
|
+
*/
|
|
22
|
+
export async function exportGrades(context, siteUrl, courseId, options) {
|
|
23
|
+
const { format, itemIds, groupId, display, feedback, destDir } = options;
|
|
24
|
+
const plugin = PLUGINS[format];
|
|
25
|
+
if (!plugin)
|
|
26
|
+
throw new MoodleError(`Grades export as ${GRADE_EXPORT_FORMATS.join(", ")}; not "${format}".`, "invalid-input");
|
|
27
|
+
if (!destDir)
|
|
28
|
+
throw new MoodleError("Give exportGrades a destDir: the folder to save the file in.", "invalid-input");
|
|
29
|
+
if (!path.isAbsolute(destDir))
|
|
30
|
+
throw new MoodleError(`Give the destination folder as an absolute path: "${destDir}".`, "invalid-input");
|
|
31
|
+
if (format === "xml" && display !== undefined && display.length !== 1)
|
|
32
|
+
throw new MoodleError("An XML export writes grades in one way only: give one display.", "invalid-input");
|
|
33
|
+
// `group` is the export page's group selector, as Moodle's own menu sets it.
|
|
34
|
+
const page = await openSitePage(context, siteUrl, `grade/export/${plugin}/index.php?id=${courseId}${groupId !== undefined ? `&group=${groupId}` : ""}`);
|
|
35
|
+
try {
|
|
36
|
+
// The gradebook's navigation menu lists the export plugins the site has enabled, by their URLs.
|
|
37
|
+
const menu = await page.evaluate(() => Array.from(globalThis.document.querySelectorAll('[role="option"][data-value*="/grade/export/"]')).map((option) => option.getAttribute("data-value") ?? ""));
|
|
38
|
+
const available = GRADE_EXPORT_FORMATS.filter((f) => menu.some((url) => url.includes(`/grade/export/${PLUGINS[f]}/index.php`)));
|
|
39
|
+
if (menu.length > 0 && !available.includes(format)) {
|
|
40
|
+
throw new GradebookError(`The site doesn't export grades as ${format}; it exports as: ${available.join(", ") || "nothing"}.`);
|
|
41
|
+
}
|
|
42
|
+
if ((await page.locator(`body#page-grade-export-${plugin}-index form.mform`).count()) === 0) {
|
|
43
|
+
const refused = await moodleErrorMessage(page);
|
|
44
|
+
throw new GradebookError(`You can't export course ${courseId}'s grades as ${format}: ${refused ?? "the course doesn't exist, the format's export is disabled on the site, or you aren't allowed"}.`);
|
|
45
|
+
}
|
|
46
|
+
// XML exports only items with an ID number: the others' boxes are frozen unticked.
|
|
47
|
+
const items = await page.evaluate(() => Array.from(globalThis.document.querySelectorAll('form.mform input[type="checkbox"][name^="itemids["]')).map((e) => ({ id: Number(/\[(\d+)\]/.exec(e.name)?.[1]), exportable: !e.disabled || e.checked })));
|
|
48
|
+
const exportable = items.filter((i) => i.exportable).map((i) => i.id);
|
|
49
|
+
if (format === "xml" && exportable.length === 0) {
|
|
50
|
+
throw new GradebookError(`None of course ${courseId}'s grade items has an ID number, and an XML export only takes items with one: set them in the items' settings, or export another format.`);
|
|
51
|
+
}
|
|
52
|
+
const values = {};
|
|
53
|
+
if (itemIds !== undefined) {
|
|
54
|
+
const unknown = itemIds.filter((id) => !items.some((i) => i.id === id));
|
|
55
|
+
if (unknown.length > 0)
|
|
56
|
+
throw new GradebookError(`Course ${courseId}'s export has no grade item ${unknown.join(", ")}: it offers ${items.map((i) => i.id).join(", ")}.`);
|
|
57
|
+
const frozen = itemIds.filter((id) => !exportable.includes(id));
|
|
58
|
+
if (frozen.length > 0)
|
|
59
|
+
throw new GradebookError(`Grade item ${frozen.join(", ")} has no ID number, and an XML export only takes items with one.`);
|
|
60
|
+
for (const id of exportable)
|
|
61
|
+
values[`itemids[${id}]`] = itemIds.includes(id);
|
|
62
|
+
}
|
|
63
|
+
if (display !== undefined) {
|
|
64
|
+
if (format === "xml")
|
|
65
|
+
values.display = DISPLAY_CODES[display[0]];
|
|
66
|
+
else
|
|
67
|
+
for (const d of Object.keys(DISPLAY_CODES))
|
|
68
|
+
values[`display[${d}]`] = display.includes(d);
|
|
69
|
+
}
|
|
70
|
+
if (feedback !== undefined)
|
|
71
|
+
values.export_feedback = feedback;
|
|
72
|
+
if (format === "csv")
|
|
73
|
+
values.separator = "comma";
|
|
74
|
+
await setFields(page, values);
|
|
75
|
+
const [download] = await Promise.all([page.waitForEvent("download"), page.click('form.mform [name="submitbutton"]', { noWaitAfter: true })]);
|
|
76
|
+
const failure = await download.failure();
|
|
77
|
+
if (failure)
|
|
78
|
+
throw new GradebookError(`Moodle didn't send course ${courseId}'s grades as ${format}: ${failure}.`);
|
|
79
|
+
await mkdir(destDir, { recursive: true });
|
|
80
|
+
const saved = await freePath(destDir, fileNameFor(new URL(download.url()), `attachment; filename="${download.suggestedFilename()}"`));
|
|
81
|
+
await download.saveAs(saved);
|
|
82
|
+
return { path: saved, name: path.basename(saved), size: (await stat(saved)).size };
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await page.close().catch(() => undefined);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../../src/areas/grades/export.ts"],"names":[],"mappings":"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"}
|