@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.
- package/LICENSE +21 -0
- package/README.md +133 -2
- package/dist/areas/activities/build.d.ts +98 -0
- package/dist/areas/activities/build.js +246 -0
- package/dist/areas/activities/build.js.map +1 -0
- package/dist/areas/activities/content.d.ts +53 -0
- package/dist/areas/activities/content.js +113 -0
- package/dist/areas/activities/content.js.map +1 -0
- package/dist/areas/assignments/grader.d.ts +68 -0
- package/dist/areas/assignments/grader.js +169 -0
- package/dist/areas/assignments/grader.js.map +1 -0
- package/dist/areas/assignments/grading.d.ts +86 -0
- package/dist/areas/assignments/grading.js +162 -0
- package/dist/areas/assignments/grading.js.map +1 -0
- package/dist/areas/assignments/student.d.ts +57 -0
- package/dist/areas/assignments/student.js +115 -0
- package/dist/areas/assignments/student.js.map +1 -0
- package/dist/areas/calendar/calendar.d.ts +43 -0
- package/dist/areas/calendar/calendar.js +84 -0
- package/dist/areas/calendar/calendar.js.map +1 -0
- package/dist/areas/completion/completion.d.ts +50 -0
- package/dist/areas/completion/completion.js +112 -0
- package/dist/areas/completion/completion.js.map +1 -0
- package/dist/areas/courses/list.d.ts +59 -0
- package/dist/areas/courses/list.js +74 -0
- package/dist/areas/courses/list.js.map +1 -0
- package/dist/areas/courses/overview.d.ts +83 -0
- package/dist/areas/courses/overview.js +137 -0
- package/dist/areas/courses/overview.js.map +1 -0
- package/dist/areas/courses/state.d.ts +51 -0
- package/dist/areas/courses/state.js +45 -0
- package/dist/areas/courses/state.js.map +1 -0
- package/dist/areas/courses/structure.d.ts +89 -0
- package/dist/areas/courses/structure.js +104 -0
- package/dist/areas/courses/structure.js.map +1 -0
- package/dist/areas/forums/discussions.d.ts +57 -0
- package/dist/areas/forums/discussions.js +119 -0
- package/dist/areas/forums/discussions.js.map +1 -0
- package/dist/areas/forums/posts.d.ts +83 -0
- package/dist/areas/forums/posts.js +114 -0
- package/dist/areas/forums/posts.js.map +1 -0
- package/dist/areas/grades/gradebook.d.ts +104 -0
- package/dist/areas/grades/gradebook.js +250 -0
- package/dist/areas/grades/gradebook.js.map +1 -0
- package/dist/areas/grades/user-report.d.ts +26 -0
- package/dist/areas/grades/user-report.js +71 -0
- package/dist/areas/grades/user-report.js.map +1 -0
- package/dist/areas/h5p/h5p.d.ts +33 -0
- package/dist/areas/h5p/h5p.js +104 -0
- package/dist/areas/h5p/h5p.js.map +1 -0
- package/dist/areas/lessons/lessons.d.ts +60 -0
- package/dist/areas/lessons/lessons.js +145 -0
- package/dist/areas/lessons/lessons.js.map +1 -0
- package/dist/areas/messages/messages.d.ts +68 -0
- package/dist/areas/messages/messages.js +84 -0
- package/dist/areas/messages/messages.js.map +1 -0
- package/dist/areas/participants/participants.d.ts +57 -0
- package/dist/areas/participants/participants.js +135 -0
- package/dist/areas/participants/participants.js.map +1 -0
- package/dist/areas/questions/banks.d.ts +83 -0
- package/dist/areas/questions/banks.js +239 -0
- package/dist/areas/questions/banks.js.map +1 -0
- package/dist/areas/quizzes/attempts.d.ts +123 -0
- package/dist/areas/quizzes/attempts.js +300 -0
- package/dist/areas/quizzes/attempts.js.map +1 -0
- package/dist/areas/quizzes/results.d.ts +36 -0
- package/dist/areas/quizzes/results.js +93 -0
- package/dist/areas/quizzes/results.js.map +1 -0
- package/dist/areas/quizzes/structure.d.ts +42 -0
- package/dist/areas/quizzes/structure.js +100 -0
- package/dist/areas/quizzes/structure.js.map +1 -0
- package/dist/areas/site/info.d.ts +25 -0
- package/dist/areas/site/info.js +45 -0
- package/dist/areas/site/info.js.map +1 -0
- package/dist/areas/site/users.d.ts +56 -0
- package/dist/areas/site/users.js +89 -0
- package/dist/areas/site/users.js.map +1 -0
- package/dist/areas/surveys/choice.d.ts +38 -0
- package/dist/areas/surveys/choice.js +105 -0
- package/dist/areas/surveys/choice.js.map +1 -0
- package/dist/areas/surveys/surveys.d.ts +51 -0
- package/dist/areas/surveys/surveys.js +201 -0
- package/dist/areas/surveys/surveys.js.map +1 -0
- package/dist/browser/chromium.d.ts +15 -0
- package/dist/browser/chromium.js +53 -0
- package/dist/browser/chromium.js.map +1 -0
- package/dist/browser/cookies.d.ts +11 -0
- package/dist/browser/cookies.js +31 -0
- package/dist/browser/cookies.js.map +1 -0
- package/dist/browser/manager.d.ts +57 -0
- package/dist/browser/manager.js +177 -0
- package/dist/browser/manager.js.map +1 -0
- package/dist/browser.d.ts +53 -0
- package/dist/browser.js +167 -0
- package/dist/browser.js.map +1 -0
- package/dist/categories.d.ts +28 -0
- package/dist/categories.js +222 -0
- package/dist/categories.js.map +1 -0
- package/dist/chromiumInstaller.d.ts +15 -0
- package/dist/chromiumInstaller.js +52 -0
- package/dist/chromiumInstaller.js.map +1 -0
- package/dist/cli/args.d.ts +33 -0
- package/dist/cli/args.js +194 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands.generated.d.ts +4 -0
- package/dist/cli/commands.generated.js +2203 -0
- package/dist/cli/commands.generated.js.map +1 -0
- package/dist/cli/config.d.ts +46 -0
- package/dist/cli/config.js +107 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +19 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +4 -0
- package/dist/cli/output.js +71 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/run.d.ts +15 -0
- package/dist/cli/run.js +194 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spec.d.ts +42 -0
- package/dist/cli/spec.js +4 -0
- package/dist/cli/spec.js.map +1 -0
- package/dist/client.d.ts +217 -0
- package/dist/client.js +179 -0
- package/dist/client.js.map +1 -0
- package/dist/core/ajax.d.ts +25 -0
- package/dist/core/ajax.js +85 -0
- package/dist/core/ajax.js.map +1 -0
- package/dist/core/editor.d.ts +18 -0
- package/dist/core/editor.js +51 -0
- package/dist/core/editor.js.map +1 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +10 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/files.d.ts +16 -0
- package/dist/core/files.js +58 -0
- package/dist/core/files.js.map +1 -0
- package/dist/core/forms.d.ts +45 -0
- package/dist/core/forms.js +200 -0
- package/dist/core/forms.js.map +1 -0
- package/dist/core/http.d.ts +18 -0
- package/dist/core/http.js +22 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/inPage.d.ts +8 -0
- package/dist/core/inPage.js +10 -0
- package/dist/core/inPage.js.map +1 -0
- package/dist/core/login.d.ts +40 -0
- package/dist/core/login.js +83 -0
- package/dist/core/login.js.map +1 -0
- package/dist/core/pace.d.ts +4 -0
- package/dist/core/pace.js +21 -0
- package/dist/core/pace.js.map +1 -0
- package/dist/core/session.d.ts +55 -0
- package/dist/core/session.js +157 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/site.d.ts +8 -0
- package/dist/core/site.js +36 -0
- package/dist/core/site.js.map +1 -0
- package/dist/core/text.d.ts +21 -0
- package/dist/core/text.js +113 -0
- package/dist/core/text.js.map +1 -0
- package/dist/home.d.ts +19 -0
- package/dist/home.js +54 -0
- package/dist/home.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +11 -0
- package/dist/internals.js +15 -0
- package/dist/internals.js.map +1 -0
- package/dist/moodle/activities.d.ts +94 -0
- package/dist/moodle/activities.js +240 -0
- package/dist/moodle/activities.js.map +1 -0
- package/dist/moodle/ajax.d.ts +22 -0
- package/dist/moodle/ajax.js +91 -0
- package/dist/moodle/ajax.js.map +1 -0
- package/dist/moodle/assign.d.ts +65 -0
- package/dist/moodle/assign.js +162 -0
- package/dist/moodle/assign.js.map +1 -0
- package/dist/moodle/calendar.d.ts +41 -0
- package/dist/moodle/calendar.js +80 -0
- package/dist/moodle/calendar.js.map +1 -0
- package/dist/moodle/choice.d.ts +33 -0
- package/dist/moodle/choice.js +100 -0
- package/dist/moodle/choice.js.map +1 -0
- package/dist/moodle/completion.d.ts +48 -0
- package/dist/moodle/completion.js +108 -0
- package/dist/moodle/completion.js.map +1 -0
- package/dist/moodle/content.d.ts +43 -0
- package/dist/moodle/content.js +105 -0
- package/dist/moodle/content.js.map +1 -0
- package/dist/moodle/course.d.ts +51 -0
- package/dist/moodle/course.js +45 -0
- package/dist/moodle/course.js.map +1 -0
- package/dist/moodle/courses.d.ts +57 -0
- package/dist/moodle/courses.js +70 -0
- package/dist/moodle/courses.js.map +1 -0
- package/dist/moodle/editor.d.ts +18 -0
- package/dist/moodle/editor.js +50 -0
- package/dist/moodle/editor.js.map +1 -0
- package/dist/moodle/files.d.ts +16 -0
- package/dist/moodle/files.js +57 -0
- package/dist/moodle/files.js.map +1 -0
- package/dist/moodle/forms.d.ts +44 -0
- package/dist/moodle/forms.js +199 -0
- package/dist/moodle/forms.js.map +1 -0
- package/dist/moodle/forum.d.ts +55 -0
- package/dist/moodle/forum.js +115 -0
- package/dist/moodle/forum.js.map +1 -0
- package/dist/moodle/forumPosts.d.ts +83 -0
- package/dist/moodle/forumPosts.js +113 -0
- package/dist/moodle/forumPosts.js.map +1 -0
- package/dist/moodle/gradebook.d.ts +102 -0
- package/dist/moodle/gradebook.js +246 -0
- package/dist/moodle/gradebook.js.map +1 -0
- package/dist/moodle/grades.d.ts +24 -0
- package/dist/moodle/grades.js +67 -0
- package/dist/moodle/grades.js.map +1 -0
- package/dist/moodle/grading.d.ts +92 -0
- package/dist/moodle/grading.js +161 -0
- package/dist/moodle/grading.js.map +1 -0
- package/dist/moodle/h5p.d.ts +31 -0
- package/dist/moodle/h5p.js +100 -0
- package/dist/moodle/h5p.js.map +1 -0
- package/dist/moodle/inPage.d.ts +8 -0
- package/dist/moodle/inPage.js +10 -0
- package/dist/moodle/inPage.js.map +1 -0
- package/dist/moodle/lesson.d.ts +57 -0
- package/dist/moodle/lesson.js +140 -0
- package/dist/moodle/lesson.js.map +1 -0
- package/dist/moodle/login.d.ts +37 -0
- package/dist/moodle/login.js +77 -0
- package/dist/moodle/login.js.map +1 -0
- package/dist/moodle/messages.d.ts +66 -0
- package/dist/moodle/messages.js +80 -0
- package/dist/moodle/messages.js.map +1 -0
- package/dist/moodle/overview.d.ts +83 -0
- package/dist/moodle/overview.js +137 -0
- package/dist/moodle/overview.js.map +1 -0
- package/dist/moodle/pace.d.ts +4 -0
- package/dist/moodle/pace.js +19 -0
- package/dist/moodle/pace.js.map +1 -0
- package/dist/moodle/participants.d.ts +55 -0
- package/dist/moodle/participants.js +131 -0
- package/dist/moodle/participants.js.map +1 -0
- package/dist/moodle/questionBank.d.ts +81 -0
- package/dist/moodle/questionBank.js +235 -0
- package/dist/moodle/questionBank.js.map +1 -0
- package/dist/moodle/quiz.d.ts +117 -0
- package/dist/moodle/quiz.js +291 -0
- package/dist/moodle/quiz.js.map +1 -0
- package/dist/moodle/quizResults.d.ts +36 -0
- package/dist/moodle/quizResults.js +93 -0
- package/dist/moodle/quizResults.js.map +1 -0
- package/dist/moodle/quizStructure.d.ts +42 -0
- package/dist/moodle/quizStructure.js +100 -0
- package/dist/moodle/quizStructure.js.map +1 -0
- package/dist/moodle/session.d.ts +39 -0
- package/dist/moodle/session.js +99 -0
- package/dist/moodle/session.js.map +1 -0
- package/dist/moodle/site.d.ts +8 -0
- package/dist/moodle/site.js +36 -0
- package/dist/moodle/site.js.map +1 -0
- package/dist/moodle/siteInfo.d.ts +25 -0
- package/dist/moodle/siteInfo.js +45 -0
- package/dist/moodle/siteInfo.js.map +1 -0
- package/dist/moodle/structure.d.ts +94 -0
- package/dist/moodle/structure.js +103 -0
- package/dist/moodle/structure.js.map +1 -0
- package/dist/moodle/submission.d.ts +48 -0
- package/dist/moodle/submission.js +109 -0
- package/dist/moodle/submission.js.map +1 -0
- package/dist/moodle/survey.d.ts +47 -0
- package/dist/moodle/survey.js +197 -0
- package/dist/moodle/survey.js.map +1 -0
- package/dist/moodle/text.d.ts +21 -0
- package/dist/moodle/text.js +113 -0
- package/dist/moodle/text.js.map +1 -0
- package/dist/moodle/user.d.ts +54 -0
- package/dist/moodle/user.js +78 -0
- package/dist/moodle/user.js.map +1 -0
- package/dist/options.d.ts +30 -0
- package/dist/options.js +35 -0
- package/dist/options.js.map +1 -0
- package/dist/sessionCookies.d.ts +11 -0
- package/dist/sessionCookies.js +31 -0
- package/dist/sessionCookies.js.map +1 -0
- package/dist/testing/fakeMoodle.d.ts +55 -0
- package/dist/testing/fakeMoodle.js +103 -0
- package/dist/testing/fakeMoodle.js.map +1 -0
- package/dist/testing/gradingPanel.d.ts +6 -0
- package/dist/testing/gradingPanel.js +22 -0
- package/dist/testing/gradingPanel.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +77 -4
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { BrowserContext } from "playwright-core";
|
|
2
|
+
/** One column of Moodle 5's activities overview, as `core_courseformat_get_overview_information` gives it. */
|
|
3
|
+
interface OverviewItem {
|
|
4
|
+
key: string;
|
|
5
|
+
name: string;
|
|
6
|
+
contenttype: string;
|
|
7
|
+
alertlabel?: string;
|
|
8
|
+
alertcount?: string | number;
|
|
9
|
+
contentjson?: string | null;
|
|
10
|
+
}
|
|
11
|
+
/** A column's value, whatever exporter Moodle used for it, as text plus what it carries besides. */
|
|
12
|
+
export interface OverviewField {
|
|
13
|
+
key: string;
|
|
14
|
+
label: string;
|
|
15
|
+
text: string;
|
|
16
|
+
date?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
details?: {
|
|
19
|
+
label: string;
|
|
20
|
+
value: string;
|
|
21
|
+
}[];
|
|
22
|
+
alert?: {
|
|
23
|
+
label: string;
|
|
24
|
+
count: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Reads one column: dates (humandate), plain values, dialogs with their lines, links and their alerts. */
|
|
28
|
+
export declare function overviewField(item: OverviewItem): OverviewField;
|
|
29
|
+
/**
|
|
30
|
+
* Moodle 5's activities overview of a course for one type ("resource" for every resource type):
|
|
31
|
+
* per activity, the columns its overview page shows the person (due dates, submissions, what needs
|
|
32
|
+
* grading, their own grade…). Without a type, every type in the course's state: "resource" first,
|
|
33
|
+
* which brings pages, books, files, folders and links at once, then each type still missing.
|
|
34
|
+
*/
|
|
35
|
+
export declare function activitiesOverview(context: BrowserContext, siteUrl: string, courseId: number, type?: string): Promise<{
|
|
36
|
+
fields: OverviewField[];
|
|
37
|
+
available?: boolean | undefined;
|
|
38
|
+
visible: boolean;
|
|
39
|
+
url?: string | undefined;
|
|
40
|
+
id: number;
|
|
41
|
+
name: string;
|
|
42
|
+
type: string;
|
|
43
|
+
}[]>;
|
|
44
|
+
/**
|
|
45
|
+
* What the person has to do by a date (calendar action events: submit, attempt, grade…), from the
|
|
46
|
+
* last `pastDays` to `days` ahead, in one course or all. Teachers' grading counts come from the
|
|
47
|
+
* activities overview instead: Moodle gives them few action events.
|
|
48
|
+
*/
|
|
49
|
+
export declare function deadlines(context: BrowserContext, siteUrl: string, { courseId, days, pastDays }?: {
|
|
50
|
+
courseId?: number;
|
|
51
|
+
days?: number;
|
|
52
|
+
pastDays?: number;
|
|
53
|
+
}): Promise<{
|
|
54
|
+
action?: {
|
|
55
|
+
url?: string | undefined;
|
|
56
|
+
count?: number | undefined;
|
|
57
|
+
name: string;
|
|
58
|
+
actionable: boolean;
|
|
59
|
+
} | undefined;
|
|
60
|
+
course?: {
|
|
61
|
+
shortname?: string | undefined;
|
|
62
|
+
id: number;
|
|
63
|
+
} | undefined;
|
|
64
|
+
event: string;
|
|
65
|
+
kind: string;
|
|
66
|
+
date: string;
|
|
67
|
+
overdue: boolean;
|
|
68
|
+
type?: string | undefined;
|
|
69
|
+
activity: string;
|
|
70
|
+
activityId?: number | undefined;
|
|
71
|
+
}[]>;
|
|
72
|
+
/** What changed in a course's activities since a date, with their names from the course's state. */
|
|
73
|
+
export declare function courseUpdates(context: BrowserContext, siteUrl: string, courseId: number, since: Date): Promise<{
|
|
74
|
+
changes: {
|
|
75
|
+
items?: number | undefined;
|
|
76
|
+
time?: string | undefined;
|
|
77
|
+
what: string;
|
|
78
|
+
}[];
|
|
79
|
+
name?: string | undefined;
|
|
80
|
+
type?: string | undefined;
|
|
81
|
+
id: number;
|
|
82
|
+
}[]>;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { callService } from "../../core/ajax.js";
|
|
2
|
+
import { courseState } from "./state.js";
|
|
3
|
+
import { decodeEntities } from "../../core/text.js";
|
|
4
|
+
const plain = (html) => decodeEntities(html.replace(/<[^>]+>/g, " ")).replace(/\s+/g, " ").trim();
|
|
5
|
+
/** Reads one column: dates (humandate), plain values, dialogs with their lines, links and their alerts. */
|
|
6
|
+
export function overviewField(item) {
|
|
7
|
+
let data = {};
|
|
8
|
+
try {
|
|
9
|
+
data = JSON.parse(item.contentjson ?? "null") ?? {};
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// An exporter Moodle may add later: the label alone.
|
|
13
|
+
}
|
|
14
|
+
const type = item.contenttype.split("\\").pop() ?? "";
|
|
15
|
+
const field = { key: item.key, label: decodeEntities(item.name), text: "" };
|
|
16
|
+
if (type === "humandate" && typeof data.timestamp === "number") {
|
|
17
|
+
field.date = new Date(data.timestamp * 1000).toISOString();
|
|
18
|
+
field.text = [data.date, data.time].filter(Boolean).join(" ");
|
|
19
|
+
}
|
|
20
|
+
else if (type === "pix_icon") {
|
|
21
|
+
// A mark (answered, submitted…): what it means is in its alt text.
|
|
22
|
+
const extras = data.extras ?? [];
|
|
23
|
+
field.text = decodeEntities(extras.find((e) => e.name === "alt")?.value ?? extras.find((e) => e.name === "title")?.value ?? "");
|
|
24
|
+
}
|
|
25
|
+
else if (type === "overviewdialog") {
|
|
26
|
+
field.text = plain(String(data.buttoncontent ?? ""));
|
|
27
|
+
const items = data.items ?? [];
|
|
28
|
+
if (items.length > 0)
|
|
29
|
+
field.details = items.map((i) => ({ label: plain(i.label), value: plain(String(i.value)) }));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
field.text = plain(String(data.content ?? data.value ?? ""));
|
|
33
|
+
if (typeof data.linkurl === "string")
|
|
34
|
+
field.url = data.linkurl;
|
|
35
|
+
}
|
|
36
|
+
if (item.alertlabel)
|
|
37
|
+
field.alert = { label: decodeEntities(item.alertlabel), count: Number(item.alertcount ?? 0) };
|
|
38
|
+
return field;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Moodle 5's activities overview of a course for one type ("resource" for every resource type):
|
|
42
|
+
* per activity, the columns its overview page shows the person (due dates, submissions, what needs
|
|
43
|
+
* grading, their own grade…). Without a type, every type in the course's state: "resource" first,
|
|
44
|
+
* which brings pages, books, files, folders and links at once, then each type still missing.
|
|
45
|
+
*/
|
|
46
|
+
export async function activitiesOverview(context, siteUrl, courseId, type) {
|
|
47
|
+
let types = type ? [type] : [];
|
|
48
|
+
let cmsByType = new Map();
|
|
49
|
+
if (!type) {
|
|
50
|
+
const state = await courseState(context, siteUrl, courseId);
|
|
51
|
+
cmsByType = new Map();
|
|
52
|
+
for (const cm of state.cm)
|
|
53
|
+
cmsByType.set(cm.module, [...(cmsByType.get(cm.module) ?? []), Number(cm.id)]);
|
|
54
|
+
types = ["resource", ...cmsByType.keys()];
|
|
55
|
+
}
|
|
56
|
+
const activities = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
for (const modname of types) {
|
|
59
|
+
// Every activity of this type came with an earlier answer ("resource").
|
|
60
|
+
if (!type && modname !== "resource" && (cmsByType.get(modname) ?? []).every((id) => seen.has(id)))
|
|
61
|
+
continue;
|
|
62
|
+
let answer;
|
|
63
|
+
try {
|
|
64
|
+
answer = await callService(context, siteUrl, "core_courseformat_get_overview_information", { courseid: courseId, modname });
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
// A type without an overview (labels, some plugins) is left out when listing them all.
|
|
68
|
+
if (type)
|
|
69
|
+
throw error;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
for (const activity of answer.activities) {
|
|
73
|
+
if (seen.has(activity.cmid))
|
|
74
|
+
continue;
|
|
75
|
+
seen.add(activity.cmid);
|
|
76
|
+
const name = activity.items.find((i) => i.key === "name");
|
|
77
|
+
const nameData = JSON.parse(name?.contentjson ?? "{}") ?? {};
|
|
78
|
+
activities.push({
|
|
79
|
+
id: activity.cmid,
|
|
80
|
+
name: decodeEntities(activity.name),
|
|
81
|
+
type: activity.modname,
|
|
82
|
+
...(activity.url ? { url: activity.url } : {}),
|
|
83
|
+
visible: !nameData.hidden,
|
|
84
|
+
...(nameData.available === false ? { available: false } : {}),
|
|
85
|
+
fields: activity.items.filter((i) => i.key !== "name").map(overviewField),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return activities;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* What the person has to do by a date (calendar action events: submit, attempt, grade…), from the
|
|
93
|
+
* last `pastDays` to `days` ahead, in one course or all. Teachers' grading counts come from the
|
|
94
|
+
* activities overview instead: Moodle gives them few action events.
|
|
95
|
+
*/
|
|
96
|
+
export async function deadlines(context, siteUrl, { courseId, days = 30, pastDays = 7 } = {}) {
|
|
97
|
+
const now = Math.floor(Date.now() / 1000);
|
|
98
|
+
const window = { timesortfrom: now - pastDays * 86400, timesortto: now + days * 86400, limitnum: 50 };
|
|
99
|
+
const { events } = courseId
|
|
100
|
+
? await callService(context, siteUrl, "core_calendar_get_action_events_by_course", { courseid: courseId, ...window })
|
|
101
|
+
: await callService(context, siteUrl, "core_calendar_get_action_events_by_timesort", window);
|
|
102
|
+
return events.map((event) => {
|
|
103
|
+
const activityId = event.url ? Number(new URL(event.url).searchParams.get("id")) || undefined : undefined;
|
|
104
|
+
return {
|
|
105
|
+
...(activityId ? { activityId } : {}),
|
|
106
|
+
activity: decodeEntities(event.activityname ?? event.name),
|
|
107
|
+
...(event.modulename ? { type: event.modulename } : {}),
|
|
108
|
+
event: decodeEntities(event.name),
|
|
109
|
+
kind: event.eventtype,
|
|
110
|
+
date: new Date(event.timesort * 1000).toISOString(),
|
|
111
|
+
overdue: event.overdue === true,
|
|
112
|
+
...(event.course ? { course: { id: event.course.id, ...(event.course.shortname ? { shortname: decodeEntities(event.course.shortname) } : {}) } } : {}),
|
|
113
|
+
...(event.action ? { action: { name: decodeEntities(event.action.name), actionable: event.action.actionable !== false, ...(event.action.itemcount ? { count: event.action.itemcount } : {}), ...(event.action.url ? { url: event.action.url } : {}) } } : {}),
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** What changed in a course's activities since a date, with their names from the course's state. */
|
|
118
|
+
export async function courseUpdates(context, siteUrl, courseId, since) {
|
|
119
|
+
// One after the other: the first call may have to read the session's sesskey.
|
|
120
|
+
const { instances } = await callService(context, siteUrl, "core_course_get_updates_since", {
|
|
121
|
+
courseid: courseId,
|
|
122
|
+
since: Math.floor(since.getTime() / 1000),
|
|
123
|
+
});
|
|
124
|
+
const state = await courseState(context, siteUrl, courseId);
|
|
125
|
+
const byId = new Map(state.cm.map((cm) => [Number(cm.id), cm]));
|
|
126
|
+
return instances
|
|
127
|
+
.filter((instance) => instance.contextlevel === "module")
|
|
128
|
+
.map((instance) => {
|
|
129
|
+
const cm = byId.get(instance.id);
|
|
130
|
+
return {
|
|
131
|
+
id: instance.id,
|
|
132
|
+
...(cm ? { name: decodeEntities(cm.name), type: cm.module } : {}),
|
|
133
|
+
changes: instance.updates.map((u) => ({ what: u.name, ...(u.timeupdated ? { time: new Date(u.timeupdated * 1000).toISOString() } : {}), ...(u.itemids ? { items: u.itemids.length } : {}) })),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=overview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.js","sourceRoot":"","sources":["../../../src/areas/courses/overview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
/** `core_courseformat_get_state` as Moodle 4.0–5.2 returns it, the fields read. */
|
|
3
|
+
export interface CourseState {
|
|
4
|
+
course?: {
|
|
5
|
+
id: string;
|
|
6
|
+
baseurl?: string;
|
|
7
|
+
};
|
|
8
|
+
section: {
|
|
9
|
+
id: string;
|
|
10
|
+
number: number;
|
|
11
|
+
title: string;
|
|
12
|
+
visible?: boolean;
|
|
13
|
+
hasrestrictions?: boolean;
|
|
14
|
+
sectionurl?: string;
|
|
15
|
+
component?: string | null;
|
|
16
|
+
parentsectionid?: string | null;
|
|
17
|
+
cmlist?: string[];
|
|
18
|
+
}[];
|
|
19
|
+
cm: {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
module: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
visible: boolean;
|
|
25
|
+
uservisible?: boolean;
|
|
26
|
+
accessvisible?: boolean;
|
|
27
|
+
hascmrestrictions?: boolean;
|
|
28
|
+
sectionid: string;
|
|
29
|
+
istrackeduser?: boolean;
|
|
30
|
+
completionstate?: number;
|
|
31
|
+
isoverallcomplete?: boolean;
|
|
32
|
+
}[];
|
|
33
|
+
}
|
|
34
|
+
/** The course page's own state: sections and activities, as a JSON string (ADR-004). */
|
|
35
|
+
export declare function courseState(context: BrowserContext, siteUrl: string, courseId: number): Promise<CourseState>;
|
|
36
|
+
/** What the course page shows of an activity, in the site's language. */
|
|
37
|
+
export interface ActivityDetails {
|
|
38
|
+
id: number;
|
|
39
|
+
dates: {
|
|
40
|
+
label: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}[];
|
|
43
|
+
restriction?: string;
|
|
44
|
+
}
|
|
45
|
+
/** The activities on a course or section page: their dates and restriction texts. */
|
|
46
|
+
export declare function readActivityDetails(page: Page): Promise<ActivityDetails[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Details for the given activities: from the course page, then from the section pages of those it
|
|
49
|
+
* doesn't show (courses with one section per page).
|
|
50
|
+
*/
|
|
51
|
+
export declare function activityDetails(context: BrowserContext, siteUrl: string, courseId: number, state: CourseState): Promise<Map<number, ActivityDetails>>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { callService } from "../../core/ajax.js";
|
|
2
|
+
import { openSitePage } from "../../core/session.js";
|
|
3
|
+
/** The course page's own state: sections and activities, as a JSON string (ADR-004). */
|
|
4
|
+
export async function courseState(context, siteUrl, courseId) {
|
|
5
|
+
return JSON.parse(await callService(context, siteUrl, "core_courseformat_get_state", { courseid: courseId }));
|
|
6
|
+
}
|
|
7
|
+
/** The activities on a course or section page: their dates and restriction texts. */
|
|
8
|
+
export function readActivityDetails(page) {
|
|
9
|
+
return page.evaluate(() => {
|
|
10
|
+
const doc = globalThis.document;
|
|
11
|
+
// No helper functions in here: see the conventions on page.evaluate.
|
|
12
|
+
return Array.from(doc.querySelectorAll('[data-for="cmitem"][data-id], li.activity[data-id]')).map((item) => {
|
|
13
|
+
const dates = Array.from(item.querySelectorAll('[data-region="activity-dates"] .date-item')).map((date) => {
|
|
14
|
+
const label = (date.querySelector(".date-item-label")?.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
15
|
+
const all = (date.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
16
|
+
return { label: label.replace(/:$/, ""), value: all.slice(label.length).trim() };
|
|
17
|
+
});
|
|
18
|
+
const restriction = (item.querySelector(".availabilityinfo")?.textContent ?? "").replace(/\s+/g, " ").trim() || undefined;
|
|
19
|
+
return { id: Number(item.getAttribute("data-id")), dates, restriction };
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Details for the given activities: from the course page, then from the section pages of those it
|
|
25
|
+
* doesn't show (courses with one section per page).
|
|
26
|
+
*/
|
|
27
|
+
export async function activityDetails(context, siteUrl, courseId, state) {
|
|
28
|
+
const details = new Map();
|
|
29
|
+
const read = async (path) => {
|
|
30
|
+
const page = await openSitePage(context, siteUrl, path);
|
|
31
|
+
try {
|
|
32
|
+
for (const item of await readActivityDetails(page))
|
|
33
|
+
details.set(item.id, item);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await page.close().catch(() => undefined);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
await read(`course/view.php?id=${courseId}`);
|
|
40
|
+
const missingSections = new Set(state.cm.filter((cm) => !details.has(Number(cm.id))).map((cm) => cm.sectionid));
|
|
41
|
+
for (const sectionId of missingSections)
|
|
42
|
+
await read(`course/section.php?id=${sectionId}`);
|
|
43
|
+
return details;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/areas/courses/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { BrowserContext } from "playwright-core";
|
|
2
|
+
import { type ActivityDetails, type CourseState } from "./state.js";
|
|
3
|
+
/** Moodle's completion states (COMPLETION_INCOMPLETE … COMPLETION_COMPLETE_FAIL), by their value. */
|
|
4
|
+
export declare const COMPLETION_STATES: readonly ["incomplete", "complete", "complete-pass", "complete-fail"];
|
|
5
|
+
export type CompletionState = (typeof COMPLETION_STATES)[number];
|
|
6
|
+
export interface CourseActivity {
|
|
7
|
+
/** The course module id, which every activity function takes. */
|
|
8
|
+
id: number;
|
|
9
|
+
name: string;
|
|
10
|
+
/** The module: "page", "assign", "quiz", "forum", "resource"… */
|
|
11
|
+
type: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
/** Shown to students. */
|
|
14
|
+
visible: boolean;
|
|
15
|
+
/** The person can open it now. */
|
|
16
|
+
availableToMe: boolean;
|
|
17
|
+
restricted: boolean;
|
|
18
|
+
/** The person's completion, when the activity tracks it for them. */
|
|
19
|
+
completion?: {
|
|
20
|
+
state: CompletionState;
|
|
21
|
+
complete: boolean;
|
|
22
|
+
};
|
|
23
|
+
/** With details: as the course page shows them, in the site's language. */
|
|
24
|
+
dates?: {
|
|
25
|
+
label: string;
|
|
26
|
+
value: string;
|
|
27
|
+
}[];
|
|
28
|
+
/** With details: the restriction as the course page shows it. */
|
|
29
|
+
restriction?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CourseSection {
|
|
32
|
+
id: number;
|
|
33
|
+
number: number;
|
|
34
|
+
title: string;
|
|
35
|
+
visible: boolean;
|
|
36
|
+
restricted: boolean;
|
|
37
|
+
url?: string;
|
|
38
|
+
/** For a subsection, the section that holds it. */
|
|
39
|
+
parentSectionId?: number;
|
|
40
|
+
activities: CourseActivity[];
|
|
41
|
+
}
|
|
42
|
+
/** The state as sections with their activities, in the course's order; details merged when given. */
|
|
43
|
+
export declare function structureOf(state: CourseState, details?: Map<number, ActivityDetails>): CourseSection[];
|
|
44
|
+
/** A course's sections and activities; with `details`, each activity's dates and restriction from the course page (slower). */
|
|
45
|
+
export declare function courseStructure(context: BrowserContext, siteUrl: string, courseId: number, { details }?: {
|
|
46
|
+
details?: boolean | undefined;
|
|
47
|
+
}): Promise<CourseSection[]>;
|
|
48
|
+
export interface CourseAssignment {
|
|
49
|
+
/** The assignment's course module id. */
|
|
50
|
+
id: number;
|
|
51
|
+
name: string;
|
|
52
|
+
url: string;
|
|
53
|
+
visible: boolean;
|
|
54
|
+
section: {
|
|
55
|
+
number: number;
|
|
56
|
+
title: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** The course's assignments, in the course's order. */
|
|
60
|
+
export declare function assignmentsOf(state: CourseState): CourseAssignment[];
|
|
61
|
+
/** The course's assignments, in the course's order, with the section each is in. */
|
|
62
|
+
export declare function listAssignments(context: BrowserContext, siteUrl: string, courseId: number): Promise<CourseAssignment[]>;
|
|
63
|
+
export interface PendingActivity {
|
|
64
|
+
activityId: number;
|
|
65
|
+
name: string;
|
|
66
|
+
type: string;
|
|
67
|
+
url?: string;
|
|
68
|
+
section: {
|
|
69
|
+
number: number;
|
|
70
|
+
title: string;
|
|
71
|
+
};
|
|
72
|
+
/** Tracked and not complete, or (completion not tracked) graded and not graded yet. */
|
|
73
|
+
reason: "incomplete" | "not-graded";
|
|
74
|
+
completion?: string;
|
|
75
|
+
availableToMe: boolean;
|
|
76
|
+
dates?: {
|
|
77
|
+
label: string;
|
|
78
|
+
value: string;
|
|
79
|
+
}[];
|
|
80
|
+
restriction?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* What the person still has to do in a course: activities whose completion is tracked and not
|
|
84
|
+
* complete or, where completion isn't tracked, graded activities without a grade yet.
|
|
85
|
+
*/
|
|
86
|
+
export declare function pendingActivities(context: BrowserContext, siteUrl: string, courseId: number): Promise<{
|
|
87
|
+
completionTracked: boolean;
|
|
88
|
+
pending: PendingActivity[];
|
|
89
|
+
}>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { activityDetails, courseState } from "./state.js";
|
|
2
|
+
import { myGrades } from "../grades/user-report.js";
|
|
3
|
+
import { decodeEntities } from "../../core/text.js";
|
|
4
|
+
/** Moodle's completion states (COMPLETION_INCOMPLETE … COMPLETION_COMPLETE_FAIL), by their value. */
|
|
5
|
+
export const COMPLETION_STATES = ["incomplete", "complete", "complete-pass", "complete-fail"];
|
|
6
|
+
/** The state as sections with their activities, in the course's order; details merged when given. */
|
|
7
|
+
export function structureOf(state, details) {
|
|
8
|
+
const cms = new Map(state.cm.map((cm) => [cm.id, cm]));
|
|
9
|
+
return state.section.map((s) => {
|
|
10
|
+
const ids = s.cmlist ?? state.cm.filter((cm) => cm.sectionid === s.id).map((cm) => cm.id);
|
|
11
|
+
const activities = ids.flatMap((id) => {
|
|
12
|
+
const cm = cms.get(id);
|
|
13
|
+
if (!cm)
|
|
14
|
+
return [];
|
|
15
|
+
const extra = details?.get(Number(cm.id));
|
|
16
|
+
const state = cm.istrackeduser && cm.completionstate !== undefined ? COMPLETION_STATES[cm.completionstate] : undefined;
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
id: Number(cm.id),
|
|
20
|
+
name: decodeEntities(cm.name),
|
|
21
|
+
type: cm.module,
|
|
22
|
+
url: cm.url,
|
|
23
|
+
visible: cm.visible,
|
|
24
|
+
availableToMe: (cm.uservisible ?? true) && (cm.accessvisible ?? true),
|
|
25
|
+
restricted: cm.hascmrestrictions ?? false,
|
|
26
|
+
completion: state ? { state, complete: cm.isoverallcomplete ?? state !== "incomplete" } : undefined,
|
|
27
|
+
dates: extra && extra.dates.length > 0 ? extra.dates : undefined,
|
|
28
|
+
restriction: extra?.restriction,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
id: Number(s.id),
|
|
34
|
+
number: s.number,
|
|
35
|
+
title: decodeEntities(s.title),
|
|
36
|
+
visible: s.visible ?? true,
|
|
37
|
+
restricted: s.hasrestrictions ?? false,
|
|
38
|
+
url: s.sectionurl,
|
|
39
|
+
parentSectionId: s.parentsectionid ? Number(s.parentsectionid) : undefined,
|
|
40
|
+
activities,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** A course's sections and activities; with `details`, each activity's dates and restriction from the course page (slower). */
|
|
45
|
+
export async function courseStructure(context, siteUrl, courseId, { details = false } = {}) {
|
|
46
|
+
const state = await courseState(context, siteUrl, courseId);
|
|
47
|
+
const extra = details ? await activityDetails(context, siteUrl, courseId, state) : undefined;
|
|
48
|
+
return structureOf(state, extra);
|
|
49
|
+
}
|
|
50
|
+
/** The course's assignments, in the course's order. */
|
|
51
|
+
export function assignmentsOf(state) {
|
|
52
|
+
const sections = new Map(state.section.map((section) => [section.id, section]));
|
|
53
|
+
return state.cm
|
|
54
|
+
.filter((cm) => cm.module === "assign")
|
|
55
|
+
.map((cm) => {
|
|
56
|
+
const section = sections.get(cm.sectionid);
|
|
57
|
+
return {
|
|
58
|
+
id: Number(cm.id),
|
|
59
|
+
name: decodeEntities(cm.name),
|
|
60
|
+
url: cm.url ?? "",
|
|
61
|
+
visible: cm.visible,
|
|
62
|
+
section: { number: section?.number ?? 0, title: decodeEntities(section?.title ?? "") },
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** The course's assignments, in the course's order, with the section each is in. */
|
|
67
|
+
export async function listAssignments(context, siteUrl, courseId) {
|
|
68
|
+
return assignmentsOf(await courseState(context, siteUrl, courseId));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What the person still has to do in a course: activities whose completion is tracked and not
|
|
72
|
+
* complete or, where completion isn't tracked, graded activities without a grade yet.
|
|
73
|
+
*/
|
|
74
|
+
export async function pendingActivities(context, siteUrl, courseId) {
|
|
75
|
+
const state = await courseState(context, siteUrl, courseId);
|
|
76
|
+
const sections = structureOf(state, await activityDetails(context, siteUrl, courseId, state));
|
|
77
|
+
const ungraded = new Set((await myGrades(context, siteUrl, courseId).catch(() => [])).filter((row) => row.kind === "item" && row.activityId && !row.grade).map((row) => row.activityId));
|
|
78
|
+
const completionTracked = state.cm.some((cm) => cm.istrackeduser && cm.completionstate !== undefined);
|
|
79
|
+
const pending = [];
|
|
80
|
+
for (const section of sections) {
|
|
81
|
+
for (const activity of section.activities) {
|
|
82
|
+
if (!activity.visible && !activity.availableToMe)
|
|
83
|
+
continue;
|
|
84
|
+
const tracked = activity.completion !== undefined;
|
|
85
|
+
const reason = tracked ? (activity.completion.complete ? undefined : "incomplete") : ungraded.has(activity.id) ? "not-graded" : undefined;
|
|
86
|
+
if (!reason)
|
|
87
|
+
continue;
|
|
88
|
+
pending.push({
|
|
89
|
+
activityId: activity.id,
|
|
90
|
+
name: activity.name,
|
|
91
|
+
type: activity.type,
|
|
92
|
+
url: activity.url,
|
|
93
|
+
section: { number: section.number, title: section.title },
|
|
94
|
+
reason,
|
|
95
|
+
completion: activity.completion?.state,
|
|
96
|
+
availableToMe: activity.availableToMe,
|
|
97
|
+
dates: activity.dates,
|
|
98
|
+
restriction: activity.restriction,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { completionTracked, pending };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../../src/areas/courses/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { BrowserContext, Page } from "playwright-core";
|
|
2
|
+
import { MoodleError } from "../../core/errors.js";
|
|
3
|
+
/** The id isn't a forum's course module, or the person can't see it. */
|
|
4
|
+
export declare class NotAForumError extends MoodleError {
|
|
5
|
+
name: string;
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/** A discussion as the forum page lists it. */
|
|
9
|
+
export interface DiscussionRow {
|
|
10
|
+
id: number;
|
|
11
|
+
subject: string;
|
|
12
|
+
author: string;
|
|
13
|
+
started?: number;
|
|
14
|
+
lastPost: {
|
|
15
|
+
author: string;
|
|
16
|
+
time?: number;
|
|
17
|
+
postId?: number;
|
|
18
|
+
};
|
|
19
|
+
replies: number;
|
|
20
|
+
locked: boolean;
|
|
21
|
+
pinned: boolean;
|
|
22
|
+
/** The forum's instance id, which the forum's services take. */
|
|
23
|
+
forumInstanceId?: number;
|
|
24
|
+
}
|
|
25
|
+
/** The discussions on a forum page, read by structure: data attributes, links, timestamps. */
|
|
26
|
+
export declare function readDiscussionRows(page: Page): Promise<DiscussionRow[]>;
|
|
27
|
+
/** Every discussion of a forum (its course module id), through all its pages. */
|
|
28
|
+
export declare function listDiscussions(context: BrowserContext, siteUrl: string, activityId: number): Promise<DiscussionRow[]>;
|
|
29
|
+
/** A post as `mod_forum_get_discussion_posts` returns it (Moodle 5.2), the fields read. */
|
|
30
|
+
export interface MoodlePost {
|
|
31
|
+
id: number;
|
|
32
|
+
subject: string;
|
|
33
|
+
message: string;
|
|
34
|
+
author: {
|
|
35
|
+
id: number;
|
|
36
|
+
fullname: string;
|
|
37
|
+
};
|
|
38
|
+
parentid: number | null;
|
|
39
|
+
timecreated: number;
|
|
40
|
+
timemodified?: number;
|
|
41
|
+
isprivatereply?: boolean;
|
|
42
|
+
unread?: boolean | null;
|
|
43
|
+
attachments?: {
|
|
44
|
+
filename: string;
|
|
45
|
+
url?: string;
|
|
46
|
+
fileurl?: string;
|
|
47
|
+
}[];
|
|
48
|
+
}
|
|
49
|
+
export declare function discussionPosts(context: BrowserContext, siteUrl: string, discussionId: number): Promise<{
|
|
50
|
+
posts: MoodlePost[];
|
|
51
|
+
forumid: number;
|
|
52
|
+
courseid: number;
|
|
53
|
+
}>;
|
|
54
|
+
/** Plain text of Moodle-cleaned HTML fragments, parsed in a blank tab. */
|
|
55
|
+
export declare function htmlToTexts(context: BrowserContext, fragments: string[]): Promise<string[]>;
|
|
56
|
+
/** A forum's instance id (what `post.php?forum=` takes), from its page. */
|
|
57
|
+
export declare function forumInstanceId(context: BrowserContext, siteUrl: string, activityId: number): Promise<number>;
|