mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
refactor: session-based incremental TFE upload via HTMX, drop SortableJS
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.
This commit is contained in:
@@ -303,6 +303,102 @@ trait ThesisFileHandler
|
||||
return $num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process TFE file uploads from session-stored temp paths.
|
||||
*
|
||||
* Used when files are uploaded incrementally via HTMX fragments (upload-tfe-file.php)
|
||||
* rather than submitted in a single multipart form.
|
||||
*
|
||||
* @param int $thesisId
|
||||
* @param array $uploads Array of ['orig_name', 'size', 'mime', 'tmp_path']
|
||||
* @param string $folderPath
|
||||
* @param string $filePrefix
|
||||
* @param int $startNum
|
||||
*/
|
||||
protected function handleTfeFilesFromSession(int $thesisId, array $uploads, string $folderPath, string $filePrefix, int $startNum = 1): int
|
||||
{
|
||||
if (empty($uploads)) {
|
||||
return $startNum;
|
||||
}
|
||||
|
||||
$dir = STORAGE_ROOT . '/' . $folderPath;
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($uploads as $f) {
|
||||
$mimeType = $f['mime'];
|
||||
$absPath = STORAGE_ROOT . '/' . $f['tmp_path'];
|
||||
|
||||
if (!file_exists($absPath)) {
|
||||
error_log("ThesisFileHandler: session temp file missing {$f['tmp_path']}, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo($f['orig_name'], PATHINFO_EXTENSION));
|
||||
|
||||
if ($mimeType === 'text/plain' && $ext === 'vtt') {
|
||||
$mimeType = 'text/vtt';
|
||||
}
|
||||
|
||||
$files[] = [
|
||||
'mimeType' => $mimeType,
|
||||
'ext' => $ext,
|
||||
'size' => $f['size'],
|
||||
'origName' => $f['orig_name'],
|
||||
'label' => '',
|
||||
'sortOrder' => null,
|
||||
'hierarchy' => $this->tfeHierarchyRank($mimeType, $ext),
|
||||
'fileType' => $this->detectFileType($mimeType, $ext),
|
||||
// Pass the absolute path so writeTfeFile knows where to copy from
|
||||
'srcPath' => $absPath,
|
||||
];
|
||||
}
|
||||
|
||||
// Sort by hierarchy rank
|
||||
usort($files, fn($a, $b) => $a['hierarchy'] - $b['hierarchy']);
|
||||
|
||||
$videoCount = 0;
|
||||
$vttQueue = [];
|
||||
|
||||
foreach ($files as $f) {
|
||||
if ($f['fileType'] === 'video') {
|
||||
$videoCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$num = $startNum;
|
||||
|
||||
foreach ($files as $f) {
|
||||
if ($f['fileType'] === 'caption') {
|
||||
$vttQueue[] = $f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($f['fileType'] === 'video') {
|
||||
$this->writeTfeFileFromSrc($f, $thesisId, $dir, $folderPath, $filePrefix, $num);
|
||||
$num++;
|
||||
|
||||
if (!empty($vttQueue)) {
|
||||
$vtt = array_shift($vttQueue);
|
||||
$this->writeTfeFileFromSrc($vtt, $thesisId, $dir, $folderPath, $filePrefix, $num);
|
||||
$num++;
|
||||
}
|
||||
} else {
|
||||
$this->writeTfeFileFromSrc($f, $thesisId, $dir, $folderPath, $filePrefix, $num);
|
||||
$num++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vttQueue as $vtt) {
|
||||
$this->writeTfeFileFromSrc($vtt, $thesisId, $dir, $folderPath, $filePrefix, $num);
|
||||
$num++;
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process annex file uploads.
|
||||
*
|
||||
@@ -421,6 +517,61 @@ trait ThesisFileHandler
|
||||
error_log("ThesisFileHandler: TFE uploaded → $targetName ({$f['fileType']})");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single TFE file from a source path (session temp) to the thesis folder.
|
||||
* Used by handleTfeFilesFromSession instead of move_uploaded_file.
|
||||
*/
|
||||
protected function writeTfeFileFromSrc(array $f, int $thesisId, string $dir, string $folderPath, string $filePrefix, int $num): void
|
||||
{
|
||||
$padded = sprintf('%02d', $num);
|
||||
$targetName = $filePrefix . '_TFE_' . $padded . '.' . $f['ext'];
|
||||
$targetPath = $dir . $targetName;
|
||||
|
||||
if (!rename($f['srcPath'], $targetPath)) {
|
||||
// Fallback: copy + unlink
|
||||
if (!copy($f['srcPath'], $targetPath)) {
|
||||
error_log("ThesisFileHandler: failed to move session TFE {$f['origName']}");
|
||||
return;
|
||||
}
|
||||
unlink($f['srcPath']);
|
||||
}
|
||||
|
||||
chmod($targetPath, 0644);
|
||||
$relPath = $folderPath . $targetName;
|
||||
|
||||
$this->db->insertThesisFile(
|
||||
$thesisId, $f['fileType'],
|
||||
$relPath,
|
||||
basename($f['origName']),
|
||||
$f['size'],
|
||||
$f['mimeType'],
|
||||
$f['label'] !== '' ? $f['label'] : null,
|
||||
$f['sortOrder']
|
||||
);
|
||||
error_log("ThesisFileHandler: TFE (session) moved → $targetName ({$f['fileType']})");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up session upload temp files and clear the session entry.
|
||||
* Call after successful commit of TFE files.
|
||||
*/
|
||||
protected function cleanupSessionUploads(): void
|
||||
{
|
||||
$sessionId = session_id();
|
||||
$tempDir = STORAGE_ROOT . '/uploads/' . $sessionId;
|
||||
|
||||
// Remove any remaining files in the temp dir
|
||||
if (is_dir($tempDir)) {
|
||||
$files = glob($tempDir . '/*');
|
||||
foreach ($files as $file) {
|
||||
@unlink($file);
|
||||
}
|
||||
@rmdir($tempDir);
|
||||
}
|
||||
|
||||
unset($_SESSION['tfe_uploads']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a hierarchy rank for sorting TFE files.
|
||||
* Lower = earlier in the sequence.
|
||||
|
||||
Reference in New Issue
Block a user