Drops the session-backed HTMX incremental upload system in favour of a
single JS module that manages `File` objects client-side and injects
them into `FormData` on submit.
Key changes:
* `file-upload-queue.js`: client-side queues with validation, reorder
(SortableJS), removal, dirty-state tracking, and fetch-based submit
with manual redirect handling
* `fichiers-fragment.php`: empty queue containers for JS-managed queues;
HTMX format switching still works with queue rehydration after swap;
annexe uploads now support multiple files
* Form UI cleanup: moved existing files and cover preview into the
`Fichiers` fieldset (edit mode); removed redundant queue labels while
keeping labels for single-file inputs (`couverture`,
`note d'intention`); added delete buttons for existing files
* `ThesisFileHandler.php`: added
`handleTfeQueueFiles()`/`handleAnnexeQueueFiles()` reading from
`$_FILES['queue_file']`; introduced `extractFilesSubArray()` for
nested upload arrays; removed session-based queue handling
* `ThesisCreateController.php` &
`ThesisEditController.php`: switched to extracted
`['queue_file']` uploads
* `beforeunload-guard.js`: now also watches
`window.__xamxamDirty`
* Deleted obsolete PHP upload/remove/reorder queue endpoints for
`partage` and `admin`
* Cleaned up route dispatch in `partage/index.php`
* Misc form and styling updates in templates/CSS
* Added `docs/cms-migration-plan.html`
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.
- Replace mb_strlen/mb_substr/mb_strtolower with strlen/substr/strtolower
(mbstring extension missing on server, causing fatal error)
- Scope annexes checkbox HTMX swap to #annexes-input-block with hx-select
(prevents duplicating entire page inside Fichiers fieldset)
- Split format+fichiers response: #format-fichiers-block (stable) and
#format-extras-block (swappable, inside Fichiers fieldset). Format
checkboxes use hx-select to extract only the extras, preserving file queue.
- Keep format extras inline in Fichiers fieldset (no sub-fieldsets). Remove
website legend input (URL only).
- When PeerTube upload disabled, show direct file upload inputs for
video/audio (name=files[]).
- Add "Glissez-déposez" sort hint below TFE file queue.
- Fix .fq-name overflow with width:0;min-width:100% chain.
- Remove legend placeholder from .fq-item.
- Merge "Récits et expérimentation" AP into "Narration Spéculative".
Rename PACS to "Pratique de lart - outils critiques, arts et contexte
simultanés".
- Remove président·e field from jury fieldset, form templates, and
controller validation. Keep DB column and display logic for existing data.
- Edit mode now uses the same fichiers-fragment.php as add and partage,
instead of duplicating the format checkboxes + new-file upload + website
URL fieldsets.
- Edit-only elements (existing files list, cover replace) stay in
a separate #edit-existing-files-block below the shared fragment.
- Removed .zip/.tar/.gz from the main TFE upload accept in both
fichiers-fragment.php and fieldset-files.php. Archives go only
in the Annexes file input.
- Removed admin/format-website-fragment.php dependency from edit
(no longer needed — the shared fragment handles website too).
fix: jury repop crash + hx-preserve on file inputs, remove zip/tar from tfe accept
- Jury fieldset add-mode repopulation now handles both scalar (legacy)
and array (new dynamic multi-row) values for jury_promoteur and
jury_promoteur_ulb_name. htmlspecialchars() was choking on array value.
- All file inputs in fichiers-fragment.php wrapped in hx-preserve
containers so HTMX swaps don't wipe user-selected files when toggling
formats or the annexes checkbox.
- Removed .zip/.tar/.gz from main TFE file accept — archives only via
annexes input (which already had multiple + correct accept).
- Edit mode now reuses the same fichiers-fragment.php fragment.
fix: file inputs re-initialize after HTMX swap via inline script
- Exposed window.XamxamInitFileUploads from file-upload-queue.js IIFE
so HTMX fragments can trigger re-binding without a global listener.
- fichiers-fragment.php emits <script>XamxamInitFileUploads()</script>
at the end of the #format-fichiers-block fragment.
- Removed hx-preserve wrappers — they prevented re-render after
format/annexes toggles changed visible inputs.
- This also fixes .zip removal from TFE accept and jury repopulation
array crash from the previous commit.
refactor: simplify file-upload-queue.js, remove file-preview.js
- file-upload-queue.js rewritten from ~250 lines to ~120 lines:
no more DataTransfer machinery, no IIFE wrapper, uses .onchange
instead of addEventListener for simpler HTMX re-init.
- window.XamxamInitFileUploads is the function itself (not an IIFE export).
- Merged file-preview.js functionality into file-upload-queue.js
(single-file .data-preview handling). Deleted file-preview.js.
- fichiers-fragment.php inline script calls XamxamInitFileUploads()
after every HTMX swap (same as before).
debug: add console.log to file-upload-queue.js for file input behavior
Adds logging at key points to diagnose why only one file is displayed:
- XamxamInitFileUploads called
- TFE queue picker init (id, multiple attribute state)
- onchange event (files count, names)
- fileArray post-concat length
- Single-file preview bindings (id, multiple attribute)
Remove after debug session.
- Removed the `vimeo/psalm` dependency and all related files
(`psalm.xml`, `psalm‑baseline.xml`, suppress annotations).
- Added **PHPStan** (v2.1.54) and **PHP‑CS‑Fixer** (v3.95.1) to
`vendor/bin/`.
- Created `phpstan.neon` (level 5, bootstraps `app/bootstrap.php`,
scans `Parsedown.php`).
- Created `phpstan‑baseline.neon` with 10 pre‑existing errors.
- Added `.php‑cs‑fixer.dist.php` (PSR‑12 + PHP80Migration, targets
`app/src` & `app/tests`).
- Added `biome.json` and updated `justfile` to replace the old Psalm
recipes with `phpstan`, `cs‑check`, and `cs‑fix`.
- Updated `.gitignore` to exclude PHPStan and PHP‑CS‑Fixer cache files.
- Updated several JS files (`file‑preview.js`, `file‑upload‑queue.js`)
eand PHP controllers (`MediaController.php`, `SearchController.php`,
`SystemController.php`).
- Minor adjustments to `TODO.md`, `app/src/Database.php`,
`app/src/Parsedown.php`, `app/src/ShareLink.php`, and
`app/src/SmtpRelay.php`.