# XAMXAM TODO ## Completed - [x] PeerTube integration — two parallel systems (backup direct upload + PeerTube API) - [x] `PeerTubeService.php` — credentials CRUD + OAuth2 password grant + multipart upload to `/api/v1/videos/upload` - [x] Migration `021_peertube_settings.sql` — `peertube_settings` table (singleton) + `peertube_upload_enabled` feature flag (default 0 = disabled) - [x] `actions/settings.php` — `peertube` section handler (toggle + credential save) - [x] `admin/parametres.php` — PeerTube section UI (instance URL, username, password, channel ID, privacy) - [x] `templates/admin/parametres.php` — PeerTube settings form between SMTP and admin account sections - [x] `admin/partage/fichiers-fragment.php` — shows `` for video/audio when enabled, keeps TODO notice when disabled - [x] `ThesisCreateController` — `handlePeerTubeUpload()` uploads video/audio to PeerTube, stores watch URL as `thesis_files` row - [x] `ThesisEditController` — same `handlePeerTubeUpload()` method for edit workflow - [x] `templates/public/tfe.php` — renders PeerTube iframe embed for files whose path contains `/videos/watch/` - [x] `AdminLogger` — `logPeerTubeUpdate()` audit method - [x] Direct file upload fallback: when `peertube_upload_enabled = 0`, standard `` + local storage works unchanged - [x] Fix `just serve` — justfile shebang recipes (`deploy-env`, `reencrypt-password`) used space indentation instead of tabs, causing "extra leading whitespace" parse error - [x] PDF 100 MB limit + bentopdf mention - [x] `ThesisCreateController`: `MAX_PDF_SIZE = 100 MB`; PDFs checked against it, other files still 500 MB - [x] `ThesisEditController`: same per-PDF limit applied - [x] `fichiers-fragment.php`: note d'intention and TFE hints mention 100 MB PDF limit + bentopdf.com link - [x] `form.php` edit-mode new-files hint updated - [x] `file-field.php`: added `$hintRaw` flag to allow HTML in hints - [x] Format types: reorder, rename, add Image/Écriture - [x] Migration 019: add Écriture - [x] Migration 020: add `sort_order` column, rename Autre → Etc. / Autre, add Image, set display order (Écriture · Image · Audio · Vidéo · Site web · Performance · Objet éditorial · Installation · Etc. / Autre) - [x] `Database.php` format_types query uses `ORDER BY sort_order, id` - [x] `fichiers-fragment.php` uses `ORDER BY sort_order, id`; Image/Vidéo/Audio IDs resolved via name map - [x] TODO: Vidéo + Audio — PeerTube API upload (notice shown in form for now) - [x] Combined Format + Fichiers into HTMX-swappable block - [x] `partage/fichiers-fragment.php` — new combined fragment: format checkboxes + fichiers fieldset that adapts based on selected formats (upload inputs / URL fields / both) - [x] Route `/partage/fichiers-fragment` added to `partage/index.php` - [x] `admin/fichiers-fragment.php` — admin-gated wrapper for the same fragment (sets `admin_mode=1`) - [x] `admin/format-website-fragment.php` — admin-gated fragment for edit-mode website URL fieldset toggle - [x] `form.php` — add/partage mode: replaced separate Format + Fichiers + website-url-fieldset with single `#format-fichiers-block` server-rendered via shared fragment - [x] `form.php` — edit mode: Format checkboxes wire to `admin/format-website-fragment.php` → `#edit-website-url-fieldset` (existing-file management untouched) - [x] `checkbox-list.php` — added `$hxInclude` variable (defaults to `'this, #website-url-fieldset'`) so callers can customise included fields - [x] TDD analysis + new test suites - [x] **Bug fixed**: `SearchController::handleSearch()` — `$coverMap` undefined variable + never populated for search results - [x] `ShareLinkTest` (13 tests) — `generateSlug`, all `validateLink` branches, `verifyPassword`, `incrementUsage`, `objet_restriction` - [x] `PureLogicTest` (31 tests) — `TfeController` helpers (meta, OG image, jury split, captions), `ThesisCreateController` helpers (autofocus, detectFileType, authorSlug), `ThesisEditController::buildFileSizeInfo`, `ExportController` CSV column consistency, `SearchController` coverMap regression - [x] Private helpers promoted to `protected` in `TfeController`, `ThesisCreateController`, `ThesisEditController` to enable subclass-based testing without reflection - [x] Form save audit + TDD - [x] `createThesis()` missing `duration_pages`/`duration_minutes` columns — fixed - [x] `ThesisCreateController` not passing raw page/minute values to `createThesis()` — fixed (`durationPages`, `durationMinutes` extracted and passed) - [x] `FormSaveTest.php` — 14 red-green tests covering create+edit round-trips for all fields - [x] Language form improvements - [x] Add Néerlandais as default language option (schema + migration 017) - [x] `language_autre` conditionally required via HTMX fragment (replaced custom JS) - [x] `language_autre` saved via `getOrCreateLanguage()` in both create and edit controllers - [x] `formData['languages']` wired in edit.php so checkboxes are pre-checked - [x] `duration_pages`/`duration_minutes` saved in `updateThesis()` and read back in `getThesisRawFields()` - [x] `beforeunload-guard` applied to add and partage forms too - [x] Audit + fix direct PHP URL references blocked by nginx catch-all `deny all` - [x] `/request-access.php` fetch in `tfe.php` → `/request-access` - [x] `/media.php?path=` in `form.php` (×2) and `admin/recapitulatif.php` → `/media?path=` - [x] Fix 403 on `/language-autre-fragment.php` from `edit.php` - [x] Root cause: standalone root-level PHP file blocked by nginx catch-all `deny all` - [x] Moved logic to `partage/language-autre-fragment.php` (shared include) - [x] Added route `/partage/language-autre-fragment` in `partage/index.php` - [x] Added `admin/language-autre-fragment.php` (AdminAuth gated, includes shared logic) - [x] `form.php` picks URL based on `$mode` (`partage` vs admin) - [x] Deleted `public/language-autre-fragment.php`; nginx unchanged - [x] Merge banner images into cover images - [x] Migration 016: copy `storage/banners/*` → `storage/covers/`, insert `thesis_files` cover records, clear `banner_path`, remove banners dir - [x] Remove banner fieldset from edit form (`form.php`) - [x] Remove banner fieldset from student submission form (`fieldset-files.php`: rename to couverture) - [x] Update `ThesisEditController::save()` — remove banner upload/removal logic - [x] Update `ThesisCreateController::submit()` — remove `handleBannerUpload` call - [x] Update `Database::handleCoverUpload()` — add webp support, raise limit to 20 MB - [x] Remove `Database::setBannerPath()`, `handleBannerUpload()`, `getThesisBannerPath()` - [x] Update `Database::deleteThesis()` / `bulkDeleteTheses()` — remove banner file cleanup - [x] `HomeController`: batch-load covers for all items, remove banner_path fallback - [x] `SearchController::handleSearch()`: batch-load covers, pass `$coverMap` to view - [x] `SearchController::handleStudentPreview()`: load covers, pass `$coverMap` to partial - [x] `TfeController::resolveOgImage()`: use cover file_type instead of banner_path - [x] `home.php`: use only `$coverMap` (no banner_path fallback) - [x] `search.php`: show cover thumbnail on result cards - [x] `student-preview.php`: use `$coverMap` instead of `banner_path` - [x] Migration applied and file moved to `applied/` - [x] Remove `required` from all form inputs in admin add/edit - [x] Introduced `$adminMode` flag in `form.php` (true when `$mode` is `'add'` or `'edit'`) - [x] Hidden "champs obligatoires" note in admin mode - [x] All `$required = true` callers in `form.php`, `fieldset-tfe-info.php`, `fieldset-academic.php`, `fieldset-licence-explanation.php`, `fieldset-files.php` changed to `!$adminMode` - [x] Hardcoded `required` HTML attributes in `fieldset-tfe-info.php` (synopsis, objet radios), `fieldset-licence-explanation.php` (access type radios), `jury-fieldset.php` (promoteur, lecteurs interne/externe) gated on `!$adminMode` - [x] Dynamic JS `ulbInput.required` in jury fieldset also gated