"use strict"; function getApplicationId() { const match = window.location.hash.match(/^#\/job-procedures\/record\/([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})/) if (match != null) { return match[1] } throw "Unable to retrieve Application ID"; } async function getXSRFToken() { // we need to go through the background script to get the XSRF-TOKEN cookie return browser.runtime.sendMessage( { type: "COOKIE", url: "https://personal.uni-graz.at", key: "XSRF-TOKEN" } ).then((cookie) => { return cookie.value; }) } async function getApplicant(applicant, token) { return content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/${applicant.id}`, { headers: { "X-XSRF-TOKEN": token } }) .then((response) => { if (response.ok) { return response.json() } throw `Failed to get applicant ${applicant.id}` }) .then(async (jsonData) => { const files = jsonData.application_files ?? [] await Promise.all(files.map(async (afile) => { const response = await content.fetch( `https://personal.uni-graz.at/api/erec/download-file/${afile.file_id}`, { credentials: "same-origin", headers: { "X-XSRF-TOKEN": token } } ) if (!response.ok) { throw `Failed to download file ${afile.file_id} for applicant ${applicant.id}` } afile.blob = await response.blob() })) return jsonData }) } async function getApplicants() { const aid = getApplicationId() const token = await getXSRFToken() return content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/procedure/${aid}`, { credentials: "same-origin", headers: { "X-XSRF-TOKEN": token } } ) .then((response) => { if (response.ok) { return response.json() } throw "Failed to get list of applications" }) .then((jsonData) => { const sliced = jsonData.slice(0, 2); return Promise.allSettled(sliced.map(async (applicant) => getApplicant(applicant, token))) }) } function sanitizeZipPathSegment(value, fallback = "unnamed") { const sanitized = String(value ?? "") .replace(/[\/\\:*?"<>|]/g, "_") .trim() return sanitized === "" ? fallback : sanitized } function downloadBlob(blob, fileName) { const url = URL.createObjectURL(blob) const link = document.createElement("a") link.href = url link.download = fileName link.click() setTimeout(() => URL.revokeObjectURL(url), 0) } function rip(event) { const btn = event.target; btn.textContent = "working ..." btn.classList.add("loading") btn.disabled = true; getApplicants() .then(async (applicants) => { const zip = new JSZip() applicants.forEach((result) => { if (result.status !== "fulfilled") { throw result.reason } const applicant = result.value const dirName = `${applicant.last_name}_${applicant.first_name}` // const dirName = `${sanitizeZipPathSegment(applicant.last_name)}_${sanitizeZipPathSegment(applicant.first_name)}` const applicantDir = zip.folder(dirName) const files = applicant.application_files ?? [] files.forEach((afile) => { if (afile.blob.size > 2_000_000) return; const ext = afile.file_name.split(".").slice(-1) const filename = `${afile.field_name}.${ext}` // const filename = sanitizeZipPathSegment(afile.file_name) console.log(filename) console.log(afile.blob) applicantDir.file(filename, afile.blob) }) }) const zipBlob = await zip.generateAsync({ type: "blob" }) downloadBlob(zipBlob, `applications_${getApplicationId()}.zip`) btn.textContent = "done" }) // .catch((error) => { // console.error(error) // btn.textContent = "failed" // btn.disabled = false // }) .finally(() => { btn.classList.remove("loading") }) } // injection of rip button function install() { const titleTag = document.querySelector("scrm-module-title") if (titleTag != null) { const a = document.createElement("button") a.textContent = "rip" a.classList.add("ripper-btn") a.addEventListener("click", rip, { once: true }) titleTag.append(" (", a, ")") } else { console.log("could not install button") } } // we need the observer to restore the button after each page load const contentObserver = new MutationObserver((mutations) => { mutations.forEach(mu => { for (const node of mu.removedNodes) { if (node.nodeName == "APP-FULL-PAGE-SPINNER") { install(); } } }); }) contentObserver.observe(document.querySelector("app-root"), { subtree: true, childList: true }); console.log(JSZip.support)