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

@@ -585,6 +585,222 @@ label:has(+ div > input:required)::after {
color: var(--text-tertiary);
}
/* ── TFE file upload queue (.tfe-file-queue) ────────────────────────────── */
.admin-files-fieldgroup {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.tfe-file-picker {
font-size: var(--step--1);
background: transparent;
border: 1px dashed var(--border-primary);
padding: var(--space-3xs) var(--space-2xs);
border-radius: 3px;
cursor: pointer;
font-family: inherit;
width: 100%;
}
.tfe-file-picker:hover {
border-color: var(--accent-primary);
}
.sortable-list {
list-style: none;
margin: var(--space-2xs) 0 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2xs);
}
/* New-file queue items */
.tfe-file-queue {
min-height: 0;
}
.tfe-queue-empty {
font-size: var(--step--2);
color: var(--text-tertiary);
margin: var(--space-3xs) 0 0;
}
.tfe-file-queue:not(:empty) + .tfe-queue-empty {
display: none;
}
.fq-item {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-3xs) var(--space-xs);
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 4px;
min-width: 0;
}
.fq-drag-handle,
.admin-file-drag-handle {
cursor: grab;
color: var(--text-tertiary);
font-size: 1.1rem;
line-height: 1;
padding: 0 var(--space-3xs);
flex-shrink: 0;
user-select: none;
}
.fq-drag-handle:active,
.admin-file-drag-handle:active {
cursor: grabbing;
}
.fq-ghost,
.sortable-ghost {
opacity: 0.4;
background: var(--accent-muted, #f0f0f0);
}
.fq-icon {
font-size: 1.3rem;
line-height: 1;
flex-shrink: 0;
width: 2rem;
text-align: center;
}
.fq-info {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
flex: 1;
min-width: 0;
}
.fq-name {
font-size: var(--step--1);
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fq-size {
font-size: var(--step--2);
color: var(--text-tertiary);
}
.fq-label,
.admin-file-label-input {
font-size: var(--step--2);
font-family: inherit;
background: transparent;
border: none;
border-bottom: 1px solid var(--border-primary);
padding: 2px 0;
width: 100%;
color: var(--text-primary);
border-radius: 0;
}
.fq-label:focus,
.admin-file-label-input:focus {
outline: none;
border-bottom-color: var(--accent-primary);
}
.fq-remove {
flex-shrink: 0;
}
/* ── Existing-files list (edit form) ─────────────────────────────────────── */
.admin-file-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.admin-file-list-item {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-3xs) var(--space-xs);
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 4px;
min-width: 0;
}
.admin-file-icon-col {
font-size: 1.2rem;
line-height: 1;
flex-shrink: 0;
width: 1.8rem;
text-align: center;
}
.admin-file-info {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
min-width: 0;
}
.admin-file-name {
font-size: var(--step--1);
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--text-primary);
}
a.admin-file-name {
text-decoration: underline;
text-underline-offset: 2px;
}
a.admin-file-name:hover {
color: var(--accent-primary);
}
.admin-file-meta-row {
display: flex;
align-items: center;
gap: var(--space-2xs);
flex-wrap: wrap;
}
.admin-file-type-badge {
font-size: var(--step--2);
padding: 1px 5px;
background: var(--bg-primary);
border: 1px solid var(--border-primary);
border-radius: 3px;
color: var(--text-secondary);
white-space: nowrap;
}
.admin-file-size {
font-size: var(--step--2);
color: var(--text-tertiary);
}
.admin-file-delete {
flex-shrink: 0;
margin-left: auto;
white-space: nowrap;
}
/* ── Recap file list (admin & partage recapitulatif) ────────────────────── */
.recap-file-list {
list-style: none;

View File

@@ -151,6 +151,53 @@ aside figcaption {
margin: var(--space-3xs) 0 0;
}
/* Audio player */
.tfe-audio {
width: 100%;
margin: 0;
display: block;
}
/* Download-only files */
.tfe-download-file {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-s) var(--space-m);
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
border-radius: 6px;
flex-wrap: wrap;
}
.tfe-download-link {
display: flex;
align-items: center;
gap: var(--space-xs);
font-size: var(--step--1);
font-weight: 500;
color: var(--text-primary);
text-decoration: underline;
text-underline-offset: 2px;
word-break: break-all;
}
.tfe-download-link:hover {
color: var(--accent-primary);
}
.tfe-download-icon {
font-size: 1.3rem;
line-height: 1;
flex-shrink: 0;
}
.tfe-download-size {
font-size: var(--step--2);
color: var(--text-tertiary);
margin-left: auto;
}
.tfe-pdf-fallback a {
color: var(--text-primary);
text-decoration: underline;