import { assert } from "./assert.ts"; import { getCourseId, CourseLocalStorage, getSesskey } from "./utils-ENV.js"; const DB_TEMPLATE_LIST_HEADER = ` `; const DB_TEMPLATE_LIST_ENTRY = ` `; const DB_TEMPLATE_LIST_FOOTER = `
ChangeId Date Coach Json
##id## ##timeadded## ##user## [[Json]]
`; let graduationDb = findGraduationDb(); const ID_QUERY_PARAM_REGEX = /[?&]id=(\d+)/; async function findGraduationDb() { let graduationDbId = CourseLocalStorage.getItem("bycs-sol-gruaduationDbId"); if (graduationDbId !== null) { return new DB(Number(graduationDbId)); } const courseUrl = `https://lernplattform.bycs.de/course/view.php?id=${getCourseId()}`; const courseRes = await fetch(courseUrl); const courseDoc = new DOMParser().parseFromString(await courseRes.text(), "text/html"); for (const a of courseDoc.querySelectorAll("div.activityname a.aalink")) { if (a.textContent.trim() === "Graduation Datenbank") { const anchor = a as HTMLAnchorElement; const match = anchor.href.match(ID_QUERY_PARAM_REGEX); if (match === null) continue; const dbId = match[1]; CourseLocalStorage.setItem("bycs-sol-graduationDbId", dbId); return new DB(Number(dbId)); } } assert(false, "Graduation DB not found"); } interface DatabaseParams { [key: string]: string; } interface DatabaseEntry { [key: string]: string; } class DB { id: number; d: Promise | null = null; constructor(id: number, d?: number) { this.id = id; if (typeof d !== "undefined") { this.d = new Promise((resolve) => { resolve(d); }); } } async fetchEntries(params?: DatabaseParams) { return loadFromDatabaseActivity(this, params); } async saveEntry(json: string) { return addEntryToDatabaseActivity(this, json); } async deleteEntries(entryIds: [number]) { return deleteFromDatabaseActivity(await this.getD(), entryIds); } async getD() { if (this.d === null) this.d = fetchDFromDatabaseActivity(this); return this.d; } } const dRegex = /d=(\d+)/; function getDFromDbDoc(db: DB, dbDoc: Document) { const urlWithD = dbDoc.querySelector('input[name="pageurl"]')!.value; const match = dRegex.exec(urlWithD); if (match === null) { console.error(`d of database ${db.id} not found`); return -1; } return Number(match[1]); } async function fetchDFromDatabaseActivity(db: DB): Promise { let dbUrl = `${window.location.origin}/mod/data/view.php?id=${db.id}`; const dbRes = await fetch(encodeURI(dbUrl)); const dbDoc = new DOMParser().parseFromString(await dbRes.text(), "text/html"); return getDFromDbDoc(db, dbDoc); } async function loadFromDatabaseActivity(db: DB, params?: DatabaseParams) { let dbUrl = `${window.location.origin}/mod/data/view.php?id=${db.id}&mode=list&perpage=1000`; // TODO(muhq): Why does this not include entries? if (typeof params !== "undefined") { dbUrl += "&filter=1&advanced=1"; for (const param in params) { dbUrl += `&${param}=${encodeURIComponent(params[param])}`; } } const dbRes = await fetch(encodeURI(dbUrl)); const dbDoc = new DOMParser().parseFromString(await dbRes.text(), "text/html"); if (db.d === null) { db.d = new Promise((resolve) => { resolve(getDFromDbDoc(db, dbDoc)); }); } let data: any[] = []; dbDoc.querySelectorAll(".db-row").forEach((row) => { let entry: DatabaseEntry = {}; row.querySelectorAll(".db-cell").forEach((field) => { const key = field.dataset.key!; let value; // Moodle includes the full date in the title attribute of the included span if (key === "date") { value = field.querySelector("span")?.title; } else { value = field.innerText.trim(); } entry[key] = value!; }); data.push(entry); }); return data; } async function getGraduationDb() { return (await graduationDb)!; } async function addEntryToDatabaseActivity(db: DB, json: string) { // TODO(muhq): do not fetch the form const url = `/mod/data/edit.php?id=${db.id}`; const editRes = await fetch(url); const editDoc = new DOMParser().parseFromString(await editRes.text(), "text/html"); const form = editDoc.querySelector('div[role="main"] > form'); const formData = new FormData(form!); for (const key of formData.keys()) { if (key.startsWith("field_")) { formData.set(key, json); // We found our json key break; } } formData.set("saveandview", "Speichern"); return await fetch("/mod/data/edit.php", { method: "POST", body: formData, }); } async function deleteFromDatabaseActivity(d: number, entryIds: [number]) { const promises = []; assert(d != -1, "DB has no valid d yet."); for (const entryId of entryIds) { const formData = new FormData(); formData.set("d", String(d)); formData.set("sesskey", getSesskey()); formData.set("confirm", "1"); formData.set("delete", String(entryId)); promises.push( fetch("/mod/data/view.php", { method: "POST", body: formData, }), ); } return Promise.all(promises); } export { DB, DatabaseEntry, DatabaseParams, getGraduationDb };