diff --git a/TODO.md b/TODO.md index 032e375..a73c465 100644 --- a/TODO.md +++ b/TODO.md @@ -27,3 +27,10 @@ - [x] On validation error, append "envoyez un e-mail à xamxam@erg.be" to flash error message - [x] Preserve uploaded file names across validation redirects: store in session, display as warning on re-render so the student knows which files to re-select - [x] Obfuscate all email addresses and mailto: links as HTML decimal entities site-wide (EmailObfuscator class, applied in templates + Parsedown post-processing) +- [x] Fix TFE and annexes files not saved in ThesisCreateController::submit(): call handleAnnexeFiles, fix file input name mapping +- [x] Apply ALLOWED_MIME_TYPES/ALLOWED_EXTENSIONS validation in handleAnnexeFiles (same as handleTfeFiles) +- [x] Fix handleAnnexeFiles to use correct $_FILES key ('annexes' not 'files') +- [x] Add annexe handling in ThesisEditController::save() +- [x] Relax 3-keyword minimum: admin mode (create) requires 1+, edit requires 1+, student (partage) requires 3 +- [x] Add CSS for file preview items (.fp-item, .fp-thumb, .fp-icon, .fp-meta, .fp-name, .fp-size) so annexes/cover/note-intention previews wrap and display correctly +- [x] Fix TFE file input accept attribute to include video/audio/archive extensions diff --git a/app/public/assets/css/form.css b/app/public/assets/css/form.css index 2c79847..9a7b8f6 100644 --- a/app/public/assets/css/form.css +++ b/app/public/assets/css/form.css @@ -1219,3 +1219,63 @@ a.recap-file-name:hover { .tag-search-item--create.tag-search-item--highlight { background: var(--accent-muted, rgba(149, 87, 181, 0.08)); } + +/* ── File preview list (couverture, note d'intention, annexes) ───────────── */ + +.file-preview-list { + list-style: none; + margin: var(--space-3xs) 0 0; + padding: 0; + display: flex; + flex-direction: column; + gap: var(--space-3xs); +} + +.fp-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: var(--radius); + min-width: 0; + max-width: 100%; +} + +.fp-thumb { + width: 48px; + height: 36px; + object-fit: cover; + border-radius: 3px; + flex-shrink: 0; +} + +.fp-icon { + font-size: 1.3rem; + line-height: 1; + flex-shrink: 0; + width: 2rem; + text-align: center; +} + +.fp-meta { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; + min-width: 0; +} + +.fp-name { + font-size: var(--step--1); + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.fp-size { + font-size: var(--step--2); + color: var(--text-tertiary); +} diff --git a/app/public/partage/fichiers-fragment.php b/app/public/partage/fichiers-fragment.php index e0cb463..14ff15e 100644 --- a/app/public/partage/fichiers-fragment.php +++ b/app/public/partage/fichiers-fragment.php @@ -162,7 +162,7 @@ $hasAnnexesChecked = !empty($_POST['has_annexes']);
PDF (max 100 MB) · Images (JPG/PNG/GIF/WEBP). diff --git a/app/src/Controllers/ThesisCreateController.php b/app/src/Controllers/ThesisCreateController.php index 243365c..b158940 100644 --- a/app/src/Controllers/ThesisCreateController.php +++ b/app/src/Controllers/ThesisCreateController.php @@ -197,6 +197,7 @@ class ThesisCreateController $this->handleCoverUpload($thesisId, $files['couverture'] ?? null, $folderPath, $filePrefix); $this->handleNoteIntentionUpload($thesisId, $files['note_intention'] ?? null, $folderPath, $filePrefix); $nextNum = $this->handleTfeFiles($thesisId, $files['files'] ?? null, $folderPath, $filePrefix, $post, 1); + $this->handleAnnexeFiles($thesisId, $files['annexes'] ?? null, $folderPath, $filePrefix, $post); // PeerTube file rows don't go on disk, but the uploads themselves are processed separately // ── 5b. PeerTube video / audio uploads ──────────────────────────────── @@ -429,7 +430,7 @@ class ThesisCreateController if (count($keywords) > 10) { throw new Exception('Maximum 10 mots-clés autorisés.'); } - if (count($keywords) < 3) { + if (!$adminMode && count($keywords) < 3) { throw new Exception('Veuillez indiquer au moins 3 mots-clés.'); } diff --git a/app/src/Controllers/ThesisEditController.php b/app/src/Controllers/ThesisEditController.php index 56a7d7c..8d0af7b 100644 --- a/app/src/Controllers/ThesisEditController.php +++ b/app/src/Controllers/ThesisEditController.php @@ -294,8 +294,8 @@ class ThesisEditController $keywords = array_values(array_unique($keywords)); $keywords = array_filter($keywords, fn($t) => $t !== ''); $keywords = array_slice($keywords, 0, 10); - if (count($keywords) < 3) { - throw new Exception('Veuillez indiquer au moins 3 mots-clés.'); + if (count($keywords) < 1) { + throw new Exception('Veuillez indiquer au moins 1 mot-clé.'); } $this->db->setThesisTags($thesisId, $keywords); error_log('[ThesisEdit] Step 6 OK — tags=' . json_encode($keywords)); @@ -425,6 +425,11 @@ class ThesisEditController $this->handleTfeFiles($thesisId, $files['files'], $folderPath, $filePrefix, $post, $tfeCount + 1); } + // ── New annexe files upload ──────────────────────────────────────────── + if (isset($files['annexes']) && is_array($files['annexes']['name'] ?? null)) { + $this->handleAnnexeFiles($thesisId, $files['annexes'], $folderPath, $filePrefix, $post); + } + // ── PeerTube video / audio uploads ──────────────────────────────────── $this->handlePeerTubeUpload($thesisId, trim($post['titre'] ?? ''), $files, 'peertube_video'); $this->handlePeerTubeUpload($thesisId, trim($post['titre'] ?? ''), $files, 'peertube_audio'); diff --git a/app/src/Controllers/ThesisFileHandler.php b/app/src/Controllers/ThesisFileHandler.php index 634ee0e..5805401 100644 --- a/app/src/Controllers/ThesisFileHandler.php +++ b/app/src/Controllers/ThesisFileHandler.php @@ -344,10 +344,20 @@ trait ThesisFileHandler if ($mimeType === 'text/plain' && $ext === 'vtt') { $mimeType = 'text/vtt'; } + if ($mimeType === 'application/octet-stream' && !in_array($ext, self::ALLOWED_EXTENSIONS, true)) { + error_log("ThesisFileHandler: annexe extension not allowed {$uploads['name'][$i]} ($ext), skipping"); + continue; + } + if (!in_array($mimeType, self::ALLOWED_MIME_TYPES, true) + && !in_array($ext, self::ALLOWED_EXTENSIONS, true)) { + error_log("ThesisFileHandler: invalid annexe type {$uploads['name'][$i]} ($mimeType / $ext), skipping"); + continue; + } - $sizeLimit = (($mimeType === 'application/pdf' || $ext === 'pdf') ? self::MAX_PDF_SIZE : self::MAX_FILE_SIZE); + $isPdf = ($mimeType === 'application/pdf' || $ext === 'pdf'); + $sizeLimit = $isPdf ? self::MAX_PDF_SIZE : self::MAX_FILE_SIZE; if ($uploads['size'][$i] > $sizeLimit) { - error_log("ThesisFileHandler: annexe too large {$uploads['name'][$i]} (" . round($uploads['size'][$i] / 1024 / 1024) . ' MB), skipping'); + error_log("ThesisFileHandler: annexe too large {$uploads['name'][$i]} (" . round($uploads['size'][$i] / 1024 / 1024) . " MB), skipping"); continue; } diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index e30c5c9..c394e91 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -194,6 +194,19 @@ +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: roqtyzln 34d91340 "feat: obfuscate all email addresses and mailto links as HTML entities" (rebased revision) ++ $linkName = $link['name'] ?? ''; +++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: roqtyzln 34d91340 "feat: obfuscate all email addresses and mailto links as HTML entities" (rebased revision) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +- $linkName = $link['name'] ?? ''; +- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination) +\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: yztqkpzz f3b3686c "fix: TFE and annexes files not saved, plus keyword validation and file preview CSS" (rebased revision) + $linkName = $link['name'] ?? ''; + $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; + $linkLockedYear = $link['locked_year'] ?? null; ++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) ++\\\\\\\ to: yztqkpzz 9c95f5ce "fix: TFE and annexes files not saved, plus keyword validation and file preview CSS" (rebased revision) +++ $linkName = $link['name'] ?? ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ?>