feat: multi-type file upload with sort order, labels, and expanded MIME support

- DB migration 007: add sort_order + display_label to thesis_files
- Database: getThesisFiles ordered by sort_order; insertThesisFile accepts label/order;
  new reorderThesisFiles() and updateThesisFileLabel() methods
- ThesisCreateController + ThesisEditController: expand allowed MIME/exts to include
  audio (mp3/ogg/wav/flac/aac/m4a), video (webm/mov/ogv), image (gif/webp),
  archives (tar/gz), any-ext via octet-stream; max size raised to 500 MB;
  accept file_labels[] and file_orders[] POST fields; detectFileType() helper
- MediaController: expanded MIME allowlist; HTTP Range support for audio/video;
  force-download for unknown types; inline for known displayable types
- fieldset-files.php: sortable queue UI with SortableJS, per-file labels, 500 MB hint
- templates/admin/edit.php: existing files as sortable list with drag handles,
  type icons, label inputs, delete checkboxes, hidden sort-order fields
- file-upload-queue.js: new JS replacing file-preview.js — sortable new-file queue,
  per-file labels, hidden order fields on submit, backward-compat legacy preview
- tfe.php: renders audio (<audio>), all video formats, images, PDF, and
  download-only 'other' files; reads display_label; sorted by sort_order
- tfe.css + form.css: styles for audio player, download files, sortable queue,
  drag handles, file type badges, label inputs
- .htaccess + .user.ini: upload_max_filesize=512M / post_max_size=520M
This commit is contained in:
Pontoporeia
2026-04-30 13:07:09 +02:00
parent 2188ff5479
commit a83dc1c74e
17 changed files with 1026 additions and 274 deletions

View File

@@ -371,80 +371,78 @@
<?php elseif (!empty($data["files"])): ?>
<?php foreach ($data["files"] as $file): ?>
<?php
$ext = strtolower(
pathinfo($file["file_path"], PATHINFO_EXTENSION),
);
$fileType = $file["file_type"] ?? "";
if ($ext === "vtt") {
continue;
}
if ($fileType === "cover") {
continue;
$ext = strtolower(pathinfo($file["file_path"] ?? '', PATHINFO_EXTENSION));
$fileType = $file["file_type"] ?? '';
// Skip helper/internal types
if ($ext === 'vtt' || $fileType === 'caption') continue;
if ($fileType === 'cover') continue;
// Determine display category
$isImage = in_array($ext, ['jpg','jpeg','png','gif','bmp','webp'], true) || $fileType === 'image';
$isVideo = in_array($ext, ['mp4','webm','mov','ogv'], true) || $fileType === 'video';
$isAudio = in_array($ext, ['mp3','ogg','oga','wav','flac','aac','m4a'], true) || $fileType === 'audio';
$isPdf = ($ext === 'pdf') || $fileType === 'main';
$isOther = !($isImage || $isVideo || $isAudio || $isPdf);
$_vttPath = null;
if ($isVideo) {
$_vttPath = $captionFiles[$_videoIndex] ?? null;
$_videoIndex++;
}
$caption = !empty($file["display_label"]) ? $file["display_label"] : ($file["description"] ?? '');
$mediaUrl = '/media?path=' . urlencode($file["file_path"]);
$fileName = htmlspecialchars($file["file_name"] ?? basename($file["file_path"]));
?>
<figure>
<?php if ($ext === "pdf"): ?>
<iframe src="/media?path=<?= urlencode(
$file["file_path"],
) ?>"
<?php if ($isPdf): ?>
<iframe src="<?= $mediaUrl ?>"
width="100%" height="700px"
style="border:none"
title="<?= htmlspecialchars(
$file["original_name"] ??
basename($file["file_path"]),
) ?>">
title="<?= $fileName ?>">
</iframe>
<p class="tfe-pdf-fallback">
<a href="/media?path=<?= urlencode(
$file["file_path"],
) ?>&download=1">
Télécharger le PDF
</a>
<a href="<?= $mediaUrl ?>&download=1">Télécharger le PDF</a>
</p>
<?php elseif (
in_array($ext, [
"jpg",
"jpeg",
"png",
"gif",
"bmp",
"webp",
])
): ?>
<img src="/media?path=<?= urlencode(
$file["file_path"],
) ?>"
alt="<?= htmlspecialchars(
!empty($file["description"])
? $file["description"]
: $data["title"] .
" — " .
($data["authors"] ?? ""),
) ?>">
<?php elseif ($ext === "mp4"): ?>
<?php
$_vttPath = $captionFiles[$_videoIndex] ?? null;
$_videoIndex++;
?>
<?php elseif ($isImage): ?>
<img src="<?= $mediaUrl ?>"
alt="<?= htmlspecialchars($caption !== '' ? $caption : $data['title'] . ' — ' . ($data['authors'] ?? '')) ?>">
<?php elseif ($isVideo): ?>
<video width="100%" controls>
<source src="/media?path=<?= urlencode(
$file["file_path"],
) ?>" type="video/mp4">
<source src="<?= $mediaUrl ?>" type="video/<?= htmlspecialchars($ext === 'mov' ? 'mp4' : $ext) ?>">
<?php if ($_vttPath): ?>
<track kind="captions"
src="/media?path=<?= urlencode(
$_vttPath,
) ?>"
srclang="fr"
label="Sous-titres"
default>
src="/media?path=<?= urlencode($_vttPath) ?>"
srclang="fr" label="Sous-titres" default>
<?php endif; ?>
</video>
<?php elseif ($isAudio): ?>
<audio controls class="tfe-audio">
<source src="<?= $mediaUrl ?>" type="audio/<?= htmlspecialchars(match($ext) {
'mp3' => 'mpeg',
'ogg', 'oga' => 'ogg',
'wav' => 'wav',
'flac' => 'flac',
'aac' => 'aac',
'm4a' => 'mp4',
default => $ext,
}) ?>">
Votre navigateur ne supporte pas la lecture audio.
</audio>
<?php else: /* other — download only */ ?>
<div class="tfe-download-file">
<a href="<?= $mediaUrl ?>&download=1" class="tfe-download-link">
<span class="tfe-download-icon">📎</span>
<span><?= $fileName ?></span>
</a>
<?php if (!empty($file['file_size'])): ?>
<small class="tfe-download-size"><?= number_format($file['file_size'] / 1024 / 1024, 2) ?> MB</small>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($file["description"])): ?>
<figcaption><?= htmlspecialchars(
$file["description"],
) ?></figcaption>
<?php if ($caption !== '' && !$isOther): ?>
<figcaption><?= htmlspecialchars($caption) ?></figcaption>
<?php endif; ?>
</figure>
<?php endforeach; ?>