diff --git a/content.js b/content.js index fd1a565..8eeb558 100644 --- a/content.js +++ b/content.js @@ -18,7 +18,7 @@ async function getXSRFToken() { } async function getApplicant(applicant, token) { - content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/${applicant.id}`, + return content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/${applicant.id}`, { headers: { "X-XSRF-TOKEN": token } }) @@ -28,7 +28,25 @@ async function getApplicant(applicant, token) { } throw `Failed to get applicant ${applicant.id}` }) - .then((jsonData) => { + .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 }) } @@ -36,7 +54,7 @@ async function getApplicant(applicant, token) { async function getApplicants() { const aid = getApplicationId() const token = await getXSRFToken() - content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/procedure/${aid}`, + return content.fetch(`https://personal.uni-graz.at/api/erec/job-applications/procedure/${aid}`, { credentials: "same-origin", headers: { "X-XSRF-TOKEN": token } @@ -49,19 +67,73 @@ async function getApplicants() { throw "Failed to get list of applications" }) .then((jsonData) => { - const sliced = jsonData.slice(0, 10); + 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((applicants) => { - btn.textContent = "done" - }) + 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 @@ -93,3 +165,5 @@ const contentObserver = new MutationObserver((mutations) => { }) contentObserver.observe(document.querySelector("app-root"), { subtree: true, childList: true }); + +console.log(JSZip.support)