169 lines
4.7 KiB
JavaScript
169 lines
4.7 KiB
JavaScript
"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)
|