mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Fix FilePond: maxFileSize as bytes + temp files survive page reload
1. maxFileSize bug: FileValidateSize plugin overrides core's maxFileSize
setter. Core uses toBytes('1GB') = 1073741824, but plugin registers
maxFileSize as [null, Type.INT] which calls toInt('1GB') = 1.
Fix: all maxFileSize and perExtensionMaxSize values as raw bytes.
Also fix option name: fileValidateSizeFilterItem → fileValidateSizeFilter.
2. Temp file persistence: files uploaded via FilePond went to
tmp/filepond/ and vanished from the UI on page reload because
data-existing-files only included DB-persisted files.
Fix: session-track temp file_ids in handleProcess, inject via
getSessionTempFiles() into data-existing-files, teach handleLoad
to stream temp files from disk, and route JS remove → revert for hex IDs.
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -24,3 +24,5 @@
|
|||||||
- [x] Formulaire étudiant : préciser "un seul contact" dans le label, mise à jour du hint pour le format le plus court (site sans https://www., insta/mastodon avec @), adaptation de l'affichage public pour supporter ces formats courts (liens automatiques pour @pseudo → Instagram, @pseudo@instance → Mastodon, domaine nu → https://)
|
- [x] Formulaire étudiant : préciser "un seul contact" dans le label, mise à jour du hint pour le format le plus court (site sans https://www., insta/mastodon avec @), adaptation de l'affichage public pour supporter ces formats courts (liens automatiques pour @pseudo → Instagram, @pseudo@instance → Mastodon, domaine nu → https://)
|
||||||
- [x] Déplacer "Contact visible" du Backoffice vers "Informations du TFE" dans le formulaire admin edit, renommer "Identité" → "Informations du TFE" dans le récapitulatif admin
|
- [x] Déplacer "Contact visible" du Backoffice vers "Informations du TFE" dans le formulaire admin edit, renommer "Identité" → "Informations du TFE" dans le récapitulatif admin
|
||||||
- [x] Rework contenus-edit: auto-save (debounce 1.5s) sur tous les formulaires (page, form-help, contacts, sidebar links), toolbar OverType sur tous les éditeurs markdown, sidebar links dynamiques (add/remove) remplaçant les 2 liens fixes erg_site_url/source_code_url par un seul key sidebar_links avec fallback de migration
|
- [x] Rework contenus-edit: auto-save (debounce 1.5s) sur tous les formulaires (page, form-help, contacts, sidebar links), toolbar OverType sur tous les éditeurs markdown, sidebar links dynamiques (add/remove) remplaçant les 2 liens fixes erg_site_url/source_code_url par un seul key sidebar_links avec fallback de migration
|
||||||
|
- [x] Fix FilePond "Fichier trop volumineux Taille max: 1byte" — le plugin FileValidateSize surcharge le setter core de maxFileSize et parse la string "1GB" via toInt → 1 au lieu de toBytes → 1073741824. Passage de toutes les valeurs maxFileSize et perExtensionMaxSize en nombres bruts (bytes). Correction du nom d'option fileValidateSizeFilterItem → fileValidateSizeFilter. Adaptation de parseSize pour accepter les nombres.
|
||||||
|
- [x] Fix FilePond: fichiers perdus après reload — les uploads temporaires (tmp/filepond/) disparaissaient car data-existing-files ne contenait que les fichiers en DB. Ajout tracking session ($_SESSION['filepond_tmp']) dans handleProcess, injection des fichiers temporaires de la session dans data-existing-files via getSessionTempFiles(), loadTempFile() dans handleLoad pour streamer depuis tmp/, et routage remove → revert pour les hex IDs.
|
||||||
|
|||||||
@@ -45,6 +45,14 @@ if ($thesisId) {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include session temp files so uploads survive page reload
|
||||||
|
require_once APP_ROOT . '/src/FilepondHandler.php';
|
||||||
|
$tempFiles = FilepondHandler::getSessionTempFiles($queueType);
|
||||||
|
foreach ($tempFiles as $tf) {
|
||||||
|
$result[] = $tf;
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* The server returns a file_id stored as item.serverId.
|
* The server returns a file_id stored as item.serverId.
|
||||||
* 4. Form submit sends only file_ids (tiny payload), not the files themselves.
|
* 4. Form submit sends only file_ids (tiny payload), not the files themselves.
|
||||||
* 5. Type + size validation: via native FilePond options + FileValidateType/Size plugins
|
* 5. Type + size validation: via native FilePond options + FileValidateType/Size plugins
|
||||||
* plus fileValidateSizeFilterItem for per-extension size caps.
|
* plus fileValidateSizeFilter for per-extension size caps.
|
||||||
* 6. Order serialization: hidden inputs track file order using serverId (not filename).
|
* 6. Order serialization: hidden inputs track file order using serverId (not filename).
|
||||||
* 7. HTMX cleanup: generic destroyFilePondsIn(target) for all swaps, not just known IDs.
|
* 7. HTMX cleanup: generic destroyFilePondsIn(target) for all swaps, not just known IDs.
|
||||||
* 8. Edit mode: loads existing files via data-existing-files JSON + server.load.
|
* 8. Edit mode: loads existing files via data-existing-files JSON + server.load.
|
||||||
@@ -47,24 +47,27 @@
|
|||||||
labelFileTypeNotAllowed: "Format non accepté",
|
labelFileTypeNotAllowed: "Format non accepté",
|
||||||
fileValidateTypeLabelExpectedTypes:
|
fileValidateTypeLabelExpectedTypes:
|
||||||
"PDF, Images, Vidéos, Audio, VTT, Archives",
|
"PDF, Images, Vidéos, Audio, VTT, Archives",
|
||||||
maxFileSize: "1GB",
|
maxFileSize: 1073741824, // 1 GB
|
||||||
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
||||||
labelMaxFileSize: "Taille max: {filesize}",
|
labelMaxFileSize: "Taille max: {filesize}",
|
||||||
allowMultiple: true,
|
allowMultiple: true,
|
||||||
// Per-extension size limits: certain types get higher caps.
|
// Per-extension size limits: certain types get higher caps.
|
||||||
|
// Values in bytes; FileValidateSize plugin reads maxFileSize as INT,
|
||||||
|
// so numeric literals are required (string suffixes like "1GB" become
|
||||||
|
// parseInt("1GB") = 1 byte inside the plugin).
|
||||||
perExtensionMaxSize: {
|
perExtensionMaxSize: {
|
||||||
pdf: "100MB",
|
pdf: 104857600, // 100 MB
|
||||||
mp4: "8GB",
|
mp4: 8589934592, // 8 GB
|
||||||
webm: "8GB",
|
webm: 8589934592,
|
||||||
ogv: "8GB",
|
ogv: 8589934592,
|
||||||
mov: "8GB",
|
mov: 8589934592,
|
||||||
mp3: "8GB",
|
mp3: 8589934592,
|
||||||
ogg: "8GB",
|
ogg: 8589934592,
|
||||||
oga: "8GB",
|
oga: 8589934592,
|
||||||
wav: "8GB",
|
wav: 8589934592,
|
||||||
flac: "8GB",
|
flac: 8589934592,
|
||||||
aac: "8GB",
|
aac: 8589934592,
|
||||||
m4a: "8GB",
|
m4a: 8589934592,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
annexe: {
|
annexe: {
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
],
|
],
|
||||||
labelFileTypeNotAllowed: "Format non accepté",
|
labelFileTypeNotAllowed: "Format non accepté",
|
||||||
fileValidateTypeLabelExpectedTypes: "PDF, ZIP, TAR, GZ",
|
fileValidateTypeLabelExpectedTypes: "PDF, ZIP, TAR, GZ",
|
||||||
maxFileSize: "1GB",
|
maxFileSize: 1073741824, // 1 GB
|
||||||
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
||||||
labelMaxFileSize: "Taille max: {filesize}",
|
labelMaxFileSize: "Taille max: {filesize}",
|
||||||
allowMultiple: true,
|
allowMultiple: true,
|
||||||
@@ -85,7 +88,7 @@
|
|||||||
acceptedFileTypes: ["image/jpeg", "image/png", "image/webp"],
|
acceptedFileTypes: ["image/jpeg", "image/png", "image/webp"],
|
||||||
labelFileTypeNotAllowed: "Seulement JPG, PNG ou WEBP",
|
labelFileTypeNotAllowed: "Seulement JPG, PNG ou WEBP",
|
||||||
fileValidateTypeLabelExpectedTypes: "JPG, PNG, WEBP",
|
fileValidateTypeLabelExpectedTypes: "JPG, PNG, WEBP",
|
||||||
maxFileSize: "20MB",
|
maxFileSize: 20971520, // 20 MB
|
||||||
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
||||||
labelMaxFileSize: "Taille max: {filesize}",
|
labelMaxFileSize: "Taille max: {filesize}",
|
||||||
allowMultiple: false,
|
allowMultiple: false,
|
||||||
@@ -94,7 +97,7 @@
|
|||||||
acceptedFileTypes: ["application/pdf"],
|
acceptedFileTypes: ["application/pdf"],
|
||||||
labelFileTypeNotAllowed: "Seulement PDF",
|
labelFileTypeNotAllowed: "Seulement PDF",
|
||||||
fileValidateTypeLabelExpectedTypes: "PDF",
|
fileValidateTypeLabelExpectedTypes: "PDF",
|
||||||
maxFileSize: "100MB",
|
maxFileSize: 104857600, // 100 MB
|
||||||
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
||||||
labelMaxFileSize: "Taille max: {filesize}",
|
labelMaxFileSize: "Taille max: {filesize}",
|
||||||
allowMultiple: false,
|
allowMultiple: false,
|
||||||
@@ -103,7 +106,7 @@
|
|||||||
acceptedFileTypes: ["text/csv"],
|
acceptedFileTypes: ["text/csv"],
|
||||||
labelFileTypeNotAllowed: "Seulement CSV",
|
labelFileTypeNotAllowed: "Seulement CSV",
|
||||||
fileValidateTypeLabelExpectedTypes: "CSV",
|
fileValidateTypeLabelExpectedTypes: "CSV",
|
||||||
maxFileSize: "50MB",
|
maxFileSize: 52428800, // 50 MB
|
||||||
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
labelMaxFileSizeExceeded: "Fichier trop volumineux",
|
||||||
labelMaxFileSize: "Taille max: {filesize}",
|
labelMaxFileSize: "Taille max: {filesize}",
|
||||||
allowMultiple: false,
|
allowMultiple: false,
|
||||||
@@ -119,6 +122,8 @@
|
|||||||
* Parse a size string like "500MB" or "2GB" to bytes.
|
* Parse a size string like "500MB" or "2GB" to bytes.
|
||||||
*/
|
*/
|
||||||
function parseSize(str) {
|
function parseSize(str) {
|
||||||
|
// Already a number (bytes) — pass through
|
||||||
|
if (typeof str === 'number') return str;
|
||||||
var m = str.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
|
var m = str.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
|
||||||
if (!m) return 0;
|
if (!m) return 0;
|
||||||
var val = parseFloat(m[1]);
|
var val = parseFloat(m[1]);
|
||||||
@@ -277,7 +282,25 @@
|
|||||||
// FilePond appends the source value (db_id) automatically
|
// FilePond appends the source value (db_id) automatically
|
||||||
|
|
||||||
remove: (source, load, error) => {
|
remove: (source, load, error) => {
|
||||||
console.log(`[filepond] remove called | db_id=${source}`);
|
console.log(`[filepond] remove called | id=${source}`);
|
||||||
|
// Hex IDs (32 chars) → temp files → use revert endpoint
|
||||||
|
if (/^[a-f0-9]{32}$/.test(source)) {
|
||||||
|
fetch(`${base}/revert.php`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: { "X-CSRF-Token": csrfToken },
|
||||||
|
body: source,
|
||||||
|
})
|
||||||
|
.then((r) => {
|
||||||
|
console.log("[filepond] revert (from remove) response | ok=" + r.ok + " | status=" + r.status);
|
||||||
|
r.ok ? load() : error("Erreur suppression");
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error("[filepond] revert (from remove) fetch error", e);
|
||||||
|
error("Erreur réseau");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Numeric IDs → DB files → use remove endpoint
|
||||||
fetch(`${base}/remove.php`, {
|
fetch(`${base}/remove.php`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -341,10 +364,9 @@
|
|||||||
labelButtonRetryItemLoad: "Réessayer",
|
labelButtonRetryItemLoad: "Réessayer",
|
||||||
labelButtonProcessItem: "Charger",
|
labelButtonProcessItem: "Charger",
|
||||||
|
|
||||||
// ── Per-extension size validation ──────────────────────────────
|
// Per-extension size validation via FileValidateSize plugin hook.
|
||||||
// Uses fileValidateSizeFilterItem if the FileValidateSize plugin supports it.
|
|
||||||
// Falls back to beforeAddFile for silent rejection (the plugin shows the error).
|
// Falls back to beforeAddFile for silent rejection (the plugin shows the error).
|
||||||
fileValidateSizeFilterItem: (item) => {
|
fileValidateSizeFilter: (item) => {
|
||||||
var ext = getExt(item.filename);
|
var ext = getExt(item.filename);
|
||||||
if (ext && perExtMax[ext]) {
|
if (ext && perExtMax[ext]) {
|
||||||
return parseSize(perExtMax[ext]); // per-extension cap for this item
|
return parseSize(perExtMax[ext]); // per-extension cap for this item
|
||||||
@@ -352,10 +374,9 @@
|
|||||||
return parseSize(cfg.maxFileSize); // queue default
|
return parseSize(cfg.maxFileSize); // queue default
|
||||||
},
|
},
|
||||||
|
|
||||||
// Fallback: if fileValidateSizeFilterItem is not available,
|
// Fallback: beforeAddFile enforces per-extension limits (silent rejection).
|
||||||
// beforeAddFile enforces per-extension limits (silent rejection).
|
|
||||||
beforeAddFile: (item) => {
|
beforeAddFile: (item) => {
|
||||||
// This check is redundant if fileValidateSizeFilterItem works,
|
// This check is redundant if fileValidateSizeFilter works,
|
||||||
// but serves as a fallback.
|
// but serves as a fallback.
|
||||||
if (typeof item.file === "undefined") return true;
|
if (typeof item.file === "undefined") return true;
|
||||||
var f = item.file;
|
var f = item.file;
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ class FilepondHandler
|
|||||||
chmod($targetPath, 0644);
|
chmod($targetPath, 0644);
|
||||||
error_log($this->logPrefix . ':process File saved to tmp | file_id=' . $fileId . ' | path=' . $targetPath);
|
error_log($this->logPrefix . ':process File saved to tmp | file_id=' . $fileId . ' | path=' . $targetPath);
|
||||||
|
|
||||||
|
// Track temp file in session so it survives page reloads
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
$_SESSION['filepond_tmp'][$queueType] = $_SESSION['filepond_tmp'][$queueType] ?? [];
|
||||||
|
$_SESSION['filepond_tmp'][$queueType][] = $fileId;
|
||||||
|
}
|
||||||
|
|
||||||
$isPeerTubeQueue = str_starts_with($queueType, 'peertube_');
|
$isPeerTubeQueue = str_starts_with($queueType, 'peertube_');
|
||||||
$isTfeAv = ($queueType === 'tfe' && preg_match('/^(video|audio)\//', $mimeType));
|
$isTfeAv = ($queueType === 'tfe' && preg_match('/^(video|audio)\//', $mimeType));
|
||||||
$shouldPeerTube = $isPeerTubeQueue || $isTfeAv;
|
$shouldPeerTube = $isPeerTubeQueue || $isTfeAv;
|
||||||
@@ -207,7 +213,16 @@ class FilepondHandler
|
|||||||
die('Méthode non autorisée.');
|
die('Méthode non autorisée.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$dbId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT);
|
$fileId = trim($_GET['id'] ?? '');
|
||||||
|
|
||||||
|
// Hex IDs (32 chars) → temp files from tmp/filepond/
|
||||||
|
if (preg_match('/^[a-f0-9]{32}$/', $fileId)) {
|
||||||
|
$this->loadTempFile($fileId);
|
||||||
|
// loadTempFile exits; never returns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric IDs → DB files
|
||||||
|
$dbId = filter_var($fileId, FILTER_VALIDATE_INT);
|
||||||
if ($dbId === false || $dbId <= 0) {
|
if ($dbId === false || $dbId <= 0) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
die('ID invalide.');
|
die('ID invalide.');
|
||||||
@@ -355,6 +370,11 @@ class FilepondHandler
|
|||||||
die('Session invalide.');
|
die('Session invalide.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove from session tracking
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
$this->removeFromSessionTmp($fileId);
|
||||||
|
}
|
||||||
|
|
||||||
$it = new RecursiveDirectoryIterator($tmpDir, RecursiveDirectoryIterator::SKIP_DOTS);
|
$it = new RecursiveDirectoryIterator($tmpDir, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
$files_it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
|
$files_it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
|
||||||
foreach ($files_it as $file) {
|
foreach ($files_it as $file) {
|
||||||
@@ -372,6 +392,153 @@ class FilepondHandler
|
|||||||
|
|
||||||
// ── Internal helpers ──────────────────────────────────────────────────────
|
// ── Internal helpers ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get temp files for the current session and a specific queue type.
|
||||||
|
*
|
||||||
|
* Returns an array suitable for injection into FilePond's data-existing-files
|
||||||
|
* JSON attribute, so temp files survive page reloads.
|
||||||
|
*/
|
||||||
|
public static function getSessionTempFiles(string $queueType): array
|
||||||
|
{
|
||||||
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileIds = $_SESSION['filepond_tmp'][$queueType] ?? [];
|
||||||
|
if (empty($fileIds)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
$missing = [];
|
||||||
|
foreach ($fileIds as $fileId) {
|
||||||
|
$tmpDir = STORAGE_ROOT . '/tmp/filepond/' . $fileId;
|
||||||
|
$manifestPath = $tmpDir . '/manifest.json';
|
||||||
|
|
||||||
|
if (!is_dir($tmpDir) || !file_exists($manifestPath)) {
|
||||||
|
$missing[] = $fileId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = json_decode(file_get_contents($manifestPath), true);
|
||||||
|
if (!is_array($manifest)) {
|
||||||
|
$missing[] = $fileId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($manifest['session_id'] ?? '') !== session_id()) {
|
||||||
|
$missing[] = $fileId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the actual file
|
||||||
|
$actualFile = null;
|
||||||
|
$dh = opendir($tmpDir);
|
||||||
|
while (($entry = readdir($dh)) !== false) {
|
||||||
|
if ($entry === '.' || $entry === '..' || $entry === 'manifest.json') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$actualFile = $tmpDir . '/' . $entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedir($dh);
|
||||||
|
|
||||||
|
if ($actualFile === null || !file_exists($actualFile)) {
|
||||||
|
$missing[] = $fileId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = [
|
||||||
|
'source' => $fileId,
|
||||||
|
'options' => [
|
||||||
|
'type' => 'local',
|
||||||
|
'file' => [
|
||||||
|
'name' => $manifest['original_name'] ?? basename($actualFile),
|
||||||
|
'size' => (int)($manifest['size'] ?? filesize($actualFile)),
|
||||||
|
'type' => $manifest['mime'] ?? 'application/octet-stream',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up session entries for missing files
|
||||||
|
if (!empty($missing)) {
|
||||||
|
$_SESSION['filepond_tmp'][$queueType] = array_values(
|
||||||
|
array_diff($fileIds, $missing)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a temp file (hex file_id) — streams the file from tmp/filepond/.
|
||||||
|
*/
|
||||||
|
private function loadTempFile(string $fileId): never
|
||||||
|
{
|
||||||
|
$tmpDir = STORAGE_ROOT . '/tmp/filepond/' . $fileId;
|
||||||
|
$manifestPath = $tmpDir . '/manifest.json';
|
||||||
|
|
||||||
|
if (!is_dir($tmpDir) || !file_exists($manifestPath)) {
|
||||||
|
http_response_code(404);
|
||||||
|
die('Fichier temporaire introuvable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = json_decode(file_get_contents($manifestPath), true);
|
||||||
|
if (!is_array($manifest) || ($manifest['session_id'] ?? '') !== session_id()) {
|
||||||
|
http_response_code(403);
|
||||||
|
die('Session invalide.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$actualFile = null;
|
||||||
|
$dh = opendir($tmpDir);
|
||||||
|
while (($entry = readdir($dh)) !== false) {
|
||||||
|
if ($entry === '.' || $entry === '..' || $entry === 'manifest.json') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$actualFile = $tmpDir . '/' . $entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedir($dh);
|
||||||
|
|
||||||
|
if ($actualFile === null || !file_exists($actualFile)) {
|
||||||
|
http_response_code(404);
|
||||||
|
die('Fichier temporaire introuvable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mimeType = $manifest['mime'] ?? mime_content_type($actualFile);
|
||||||
|
$fileSize = filesize($actualFile);
|
||||||
|
$fileName = $manifest['original_name'] ?? basename($actualFile);
|
||||||
|
|
||||||
|
error_log($this->logPrefix . ':load TEMP | file_id=' . $fileId . ' | name=' . $fileName . ' | size=' . $fileSize);
|
||||||
|
header('Content-Type: ' . $mimeType);
|
||||||
|
header('Content-Length: ' . $fileSize);
|
||||||
|
header('Content-Disposition: inline; filename="' . addslashes($fileName) . '"');
|
||||||
|
header('Cache-Control: no-cache');
|
||||||
|
readfile($actualFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file_id from the session temp tracking array.
|
||||||
|
*/
|
||||||
|
private function removeFromSessionTmp(string $fileId): void
|
||||||
|
{
|
||||||
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($_SESSION['filepond_tmp'] ?? [] as $queueType => $ids) {
|
||||||
|
$idx = array_search($fileId, $ids, true);
|
||||||
|
if ($idx !== false) {
|
||||||
|
array_splice($_SESSION['filepond_tmp'][$queueType], $idx, 1);
|
||||||
|
if (empty($_SESSION['filepond_tmp'][$queueType])) {
|
||||||
|
unset($_SESSION['filepond_tmp'][$queueType]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the first available file from $_FILES regardless of nesting depth.
|
* Extract the first available file from $_FILES regardless of nesting depth.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,3 +14,5 @@
|
|||||||
{"timestamp":"2026-06-09T15:19:03+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"page","action":"edit","status":"success","context":{"slug":"about"}}
|
{"timestamp":"2026-06-09T15:19:03+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"page","action":"edit","status":"success","context":{"slug":"about"}}
|
||||||
{"timestamp":"2026-06-09T15:20:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"contacts"}}
|
{"timestamp":"2026-06-09T15:20:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"contacts"}}
|
||||||
{"timestamp":"2026-06-09T15:21:03+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"contacts"}}
|
{"timestamp":"2026-06-09T15:21:03+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"contacts"}}
|
||||||
|
{"timestamp":"2026-06-09T15:23:37+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"sidebar_links"}}
|
||||||
|
{"timestamp":"2026-06-09T15:23:39+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"apropos","action":"edit","status":"success","context":{"key":"sidebar_links"}}
|
||||||
|
|||||||
@@ -335,6 +335,14 @@ $_buildQueueFilesJson = function (array $files, string $queueType): array {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Include session temp files so uploads survive page reload
|
||||||
|
require_once APP_ROOT . '/src/FilepondHandler.php';
|
||||||
|
$tempFiles = FilepondHandler::getSessionTempFiles($queueType);
|
||||||
|
foreach ($tempFiles as $tf) {
|
||||||
|
$result[] = $tf;
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
};
|
};
|
||||||
if ($filesMode === 'add'): ?>
|
if ($filesMode === 'add'): ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user