Split form.css into form-base.css and form-admin.css, drop dead upload-progress code

Also introduces $extraCssAdmin support in head.php for admin-only
stylesheets (form-admin.css, filepond CSS, system.css). Admin pages
now use $extraCssAdmin for admin-only assets and $extraCss for
shared stylesheets like form-base.css.
This commit is contained in:
Pontoporeia
2026-06-11 11:04:01 +02:00
parent 99125cc8e3
commit cbd369bc72
15 changed files with 250 additions and 682 deletions

View File

@@ -1,221 +0,0 @@
/**
* upload-progress.js
*
* Intercepts admin form submissions with files and submits via XMLHttpRequest.
* Polls GET /admin/actions/upload-progress.php?token=xxx for server-side
* processing progress (PeerTube uploads, file moves).
*
* Progress display:
* 0%25% : browser → server upload (XHR upload.progress)
* 25%99% : server processing (polled from progress endpoint)
* 100% : response received — "Téléversé avec succès", then redirect
*/
(() => {
const FORMS = document.querySelectorAll("form[data-upload-progress]");
if (!FORMS.length) return;
const POLL_INTERVAL = 400;
const UPLOAD_CAP = 25;
const PROCESSING_MAX = 99;
const SUCCESS_DELAY = 800;
for (const form of FORMS) {
const progressWrap = form.querySelector("#upload-progress-wrap");
const progressBar = form.querySelector("#upload-progress-bar");
const progressLabel = form.querySelector("#upload-progress-label");
const progressFile = form.querySelector("#upload-progress-file");
const submitBtn = form.querySelector('button[type="submit"]');
const tokenInput = form.querySelector('input[name="progress_token"]');
if (!progressBar || !progressWrap) continue;
function collectFileNames() {
const names = [];
// Check raw <input type="file"> elements (non-FilePond)
const inputs = form.querySelectorAll('input[type="file"]');
for (const fi of inputs) {
if (fi.files) {
for (const f of fi.files) {
if (f.name) names.push(f.name);
}
}
}
// Read processed file names from FilePond instances (async mode)
if (typeof FilePond !== "undefined") {
const pondInputs = form.querySelectorAll(".tfe-file-picker");
for (const pi of pondInputs) {
const pond = FilePond.find(pi);
if (pond) {
const pondFiles = pond.getFiles();
for (const pf of pondFiles) {
// Only count successfully uploaded files (have serverId)
if (pf.serverId) {
const name = pf.filename || pf.file?.name || pf.serverId;
if (name) names.push(name);
}
}
}
}
}
return names;
}
form.addEventListener("submit", (e) => {
// ── Guard: block submit if any FilePond item is still uploading ──
if (typeof FilePond !== "undefined") {
let stillUploading = false;
const pondInputs = form.querySelectorAll(".tfe-file-picker");
for (const pi of pondInputs) {
const pond = FilePond.find(pi);
if (pond) {
const pondFiles = pond.getFiles();
for (const pf of pondFiles) {
if (
pf.status === FilePond.FileStatus.PROCESSING ||
pf.status === FilePond.FileStatus.IDLE
) {
stillUploading = true;
break;
}
}
}
if (stillUploading) break;
}
if (stillUploading) {
e.preventDefault();
progressLabel.textContent =
"Veuillez attendre la fin du téléversement…";
progressWrap.style.display = "";
return;
}
}
const fileNames = collectFileNames();
if (!fileNames.length) return;
e.preventDefault();
const token = tokenInput ? tokenInput.value : "";
progressWrap.style.display = "";
progressBar.value = 0;
progressBar.removeAttribute("data-complete");
progressLabel.textContent = "Téléversement en cours…";
progressFile.textContent =
fileNames.length === 1 ? fileNames[0] : `${fileNames.length} fichiers`;
if (submitBtn) submitBtn.disabled = true;
const fd = new FormData(form);
const xhr = new XMLHttpRequest();
let _uploadDone = false;
let lastUploadPct = 0;
let pollingTimer = null;
/** Poll server-side progress */
function startPolling() {
if (pollingTimer || !token) return;
progressLabel.textContent = "Traitement en cours…";
pollingTimer = setInterval(() => {
fetch(
"/admin/actions/upload-progress.php?token=" +
encodeURIComponent(token),
)
.then((r) => r.json())
.then((data) => {
if (data?.stage && data.stage !== "upload") {
const pct = Math.min(
PROCESSING_MAX,
Math.max(UPLOAD_CAP, data.pct || UPLOAD_CAP),
);
progressBar.value = pct;
if (data.file) {
progressFile.textContent = data.file;
}
}
})
.catch(() => {
/* ignore poll errors */
});
}, POLL_INTERVAL);
}
function stopPolling() {
if (pollingTimer) {
clearInterval(pollingTimer);
pollingTimer = null;
}
}
function finishSuccess() {
stopPolling();
progressBar.value = 100;
progressBar.setAttribute("data-complete", "");
progressLabel.textContent = "Téléversé avec succès";
progressFile.textContent = "";
}
// ── Upload phase (0% → UPLOAD_CAP) ──
xhr.upload.addEventListener("progress", (evt) => {
if (evt.lengthComputable) {
const rawPct = Math.round((evt.loaded / evt.total) * 100);
const scaled = Math.round((rawPct / 100) * UPLOAD_CAP);
if (scaled > lastUploadPct) {
lastUploadPct = scaled;
progressBar.value = scaled;
}
}
});
xhr.upload.addEventListener("loadend", () => {
_uploadDone = true;
progressBar.value = UPLOAD_CAP;
startPolling();
});
// ── Response handling ──
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState !== XMLHttpRequest.DONE) return;
stopPolling();
if (xhr.status >= 200 && xhr.status < 300) {
finishSuccess();
setTimeout(() => {
const finalUrl = xhr.responseURL || "";
if (finalUrl && finalUrl !== form.action) {
window.location.href = finalUrl;
} else {
document.open();
document.write(xhr.responseText);
document.close();
}
}, SUCCESS_DELAY);
} else {
progressLabel.textContent = "Erreur";
progressFile.textContent = "Échec du téléversement";
document.open();
document.write(xhr.responseText);
document.close();
}
});
xhr.addEventListener("error", () => {
stopPolling();
progressLabel.textContent = "Erreur réseau";
progressFile.textContent = "";
if (submitBtn) submitBtn.disabled = false;
});
xhr.addEventListener("abort", () => {
stopPolling();
progressWrap.style.display = "none";
if (submitBtn) submitBtn.disabled = false;
});
xhr.open("POST", form.action, true);
xhr.send(fd);
});
}
})();