mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Replace the client-side FileArray + Sortable drag-to-reorder with a
server-side session-based upload flow:
- New endpoints: /partage/upload-tfe-file, /partage/remove-tfe-file
(and /admin/ variants) — single-file incremental upload via HTMX
multipart/form-data with progress bar support
- Session storage: uploaded files go to STORAGE_ROOT/uploads/{session_id}/
with metadata in $_SESSION['tfe_uploads']
- file-upload-queue.js reduced to single-file previews only (couverture,
note_intention, annexes thumbnails)
- ThesisFileHandler gains handleTfeFilesFromSession + writeTfeFileFromSrc
+ cleanupSessionUploads for final commit from session temp
- Sortable.min.js removed from all script tags; drag handles and ghost
CSS removed
- No file_orders[]/file_labels[] hidden field injection needed
- Upload queue survives page refresh (server-owned list)
This eliminates the SortableJS dependency entirely while keeping the
same UX: pick files, see them in a queue, remove individual files.
110 lines
3.2 KiB
JavaScript
110 lines
3.2 KiB
JavaScript
/**
|
|
* file-upload-queue.js
|
|
*
|
|
* Provides single-file previews for file inputs with the data-preview attribute
|
|
* (couverture, note_intention, annexes, etc.).
|
|
*
|
|
* The multi-file TFE queue is rendered server-side via HTMX fragments
|
|
* (upload-tfe-file.php / remove-tfe-file.php).
|
|
*
|
|
* Exposes window.XamxamInitFileUploads() so HTMX fragments can re-bind
|
|
* after swap without a global event listener.
|
|
*/
|
|
window.XamxamInitFileUploads = function () {
|
|
console.log("[file-upload-queue] XamxamInitFileUploads called");
|
|
var ICON = {
|
|
pdf: "\uD83D\uDCC4",
|
|
video: "\uD83C\uDFAC",
|
|
audio: "\uD83D\uDD0A",
|
|
zip: "\uD83D\uDDDC\uFE0F",
|
|
vtt: "\uD83D\uDCAC",
|
|
image: "\uD83D\uDDBC\uFE0F",
|
|
other: "\uD83D\uDCCE",
|
|
};
|
|
|
|
function iconFor(file) {
|
|
var t = file.type || "",
|
|
n = file.name.toLowerCase();
|
|
if (/^image\//.test(t)) return ICON.image;
|
|
if (t === "application/pdf" || /\.pdf$/.test(n)) return ICON.pdf;
|
|
if (/^video\//.test(t) || /\.(mp4|webm|mov|ogv)$/.test(n))
|
|
return ICON.video;
|
|
if (/^audio\//.test(t) || /\.(mp3|ogg|oga|wav|flac|aac|m4a)$/.test(n))
|
|
return ICON.audio;
|
|
if (/\.(zip|tar|gz|tgz)$/.test(n)) return ICON.zip;
|
|
if (/\.vtt$/.test(n)) return ICON.vtt;
|
|
return ICON.other;
|
|
}
|
|
|
|
function humanSize(b) {
|
|
return b >= 1073741824
|
|
? (b / 1073741824).toFixed(2) + " GB"
|
|
: b >= 1048576
|
|
? (b / 1048576).toFixed(2) + " MB"
|
|
: b >= 1024
|
|
? (b / 1024).toFixed(1) + " KB"
|
|
: b + " B";
|
|
}
|
|
|
|
function esc(s) {
|
|
return s.replace(/[&<>"]/g, function (c) {
|
|
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
|
|
});
|
|
}
|
|
|
|
// ── Single-file previews (data-preview attribute) ────────────────────
|
|
document
|
|
.querySelectorAll('input[type="file"][data-preview]')
|
|
.forEach(function (input) {
|
|
if (input.id === "tfe-files-input") return;
|
|
console.log(
|
|
"[file-upload-queue] binding preview for",
|
|
input.id,
|
|
"multiple=",
|
|
input.multiple,
|
|
);
|
|
var container = document.getElementById(
|
|
input.getAttribute("data-preview"),
|
|
);
|
|
if (!container) return;
|
|
input.onchange = function () {
|
|
container.innerHTML = "";
|
|
Array.from(input.files).forEach(function (file) {
|
|
var item = document.createElement("div");
|
|
item.className = "fp-item";
|
|
if (/^image\//.test(file.type)) {
|
|
var img = document.createElement("img");
|
|
img.className = "fp-thumb";
|
|
img.alt = file.name;
|
|
var reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
img.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
item.appendChild(img);
|
|
} else {
|
|
var ic = document.createElement("span");
|
|
ic.className = "fp-icon";
|
|
ic.textContent = iconFor(file);
|
|
item.appendChild(ic);
|
|
}
|
|
var meta = document.createElement("span");
|
|
meta.className = "fp-meta";
|
|
meta.innerHTML =
|
|
'<span class="fp-name">' +
|
|
esc(file.name) +
|
|
'</span><span class="fp-size">' +
|
|
humanSize(file.size) +
|
|
"</span>";
|
|
item.appendChild(meta);
|
|
container.appendChild(item);
|
|
});
|
|
};
|
|
});
|
|
};
|
|
|
|
// Bootstrap on page load
|
|
if (document.readyState === "loading")
|
|
document.addEventListener("DOMContentLoaded", window.XamxamInitFileUploads);
|
|
else window.XamxamInitFileUploads();
|