* **Unified Format + Fichiers into a single HTMX fragment**
* Introduced `app/public/partage/fichiers-fragment.php` as shared dynamic block returning both format checkboxes and adaptive “Fichiers” fieldset
* Logic adapts inputs based on selected formats:
* no selection / upload formats → standard file inputs
* “Site web” → URL fields only
* “Site web + upload” → file inputs + URL sub-fieldset
* Added admin wrapper: `app/public/admin/fichiers-fragment.php` (gated via `admin_mode=1`)
* Added `app/public/admin/format-website-fragment.php` for edit-mode website URL toggling
* Wired route `/partage/fichiers-fragment` in `app/public/partage/index.php`
* Refactored `form.php` (add/edit partage) to use single `#format-fichiers-block` instead of separate fragments
* Edit mode format checkboxes now target `format-website-fragment.php` → `#edit-website-url-fieldset`
* Added `$hxInclude` support in `checkbox-list.php` for configurable HTMX includes
* **Format system migration + ordering**
* Migration `020_format_types_sort_and_rename.sql`:
* added `sort_order` column to `format_types`
* inserted new format **Image**
* defined ordering: Écriture · Image · Audio · Vidéo · Site web · Performance · Objet éditorial · Installation · Autre
* `Database.php`: format queries now use `ORDER BY sort_order, id`
* `fichiers-fragment.php`:
* uses ordered format list
* resolves Image/Vidéo/Audio by name
* introduces `$hasImage` flag
* preserves `admin_mode` across HTMX requests
* **File constraints and UX updates**
* Enforced **100 MB PDF limit**
* `ThesisCreateController`: `MAX_PDF_SIZE = 100MB` for PDFs only
* `ThesisEditController`: same PDF-specific constraint applied
* Other file types remain capped at 500 MB
* Updated UI hints in `fichiers-fragment.php` and edit form:
* explicitly mention 100 MB PDF limit
* added reference to `bentopdf.com` for compression guidance
* `file-field.php`: added `$hintRaw` to allow HTML rendering in hints
* **Admin authentication fix**
* Fixed missing auth in admin fragments
* Added `require_once AdminAuth.php`
* Replaced direct usage with `AdminAuth::requireLogin()`
* Applied consistent pattern with existing fragment authentication approach
* **Migrations included**
* `019_add_ecriture_format.sql`
* `020_format_types_sort_and_rename.sql`
* **Files affected**
* Controllers: `ThesisCreateController`, `ThesisEditController`
* DB layer: `Database.php`
* Public fragments: `partage/fichiers-fragment.php`, `admin/fichiers-fragment.php`, `admin/format-website-fragment.php`
* Templates: `form.php`, `checkbox-list.php`, `file-field.php`
* Routing: `partage/index.php`
* Misc: `TODO.md`
This consolidates format normalization, HTMX UI simplification, file validation rules, and admin stability fixes into a single coherent system update.
- fix: 403 on /language-autre-fragment.php — add explicit nginx location block
The nginx catch-all blocked direct access
to all PHP files except /index.php and files inside /admin/.
language-autre-fragment.php lives at the public root and is POSTed to by
HTMX from both the admin edit form and the partage form. Added an explicit
fastcgi block so it is executed
rather than denied.
- fix: replace .php-suffixed public URLs blocked by nginx catch-all
Audit of all client-facing PHP URL references against nginx routing:
- fetch('/request-access.php') in tfe.php -> '/request-access'
(clean URL already routed by Dispatcher)
- /media.php?path= in form.php (x2) and admin/recapitulatif.php -> /media?path=
(nginx only has location = /media, no location for /media.php)
All these .php-suffixed URLs hit the nginx catch-all
location ~ \.php$ { deny all; }
which takes precedence over location / { try_files ... } for regex matches.
Created templates/partials/form/form.php as the unified form template driven by
$mode ('add'|'edit'|'partage') and boolean flags for optional sections.
The three calling templates (templates/admin/add.php, templates/admin/edit.php,
partage/index.php renderShareLinkForm) now only set variables then include the
shared partial. ~200 lines of duplicated fieldset HTML eliminated.
The main GET handler in partage/index.php always showed the password gate
for links with password_hash set, even after successful verification. The
session flag share_verified_<slug> was being set by requirePasswordGate()
but never checked when deciding whether to re-show the gate.
Added a check: if the session flag is already set, skip the gate and
render the form directly.
Also added error_log() calls throughout the password flow to help
diagnose future issues.
- Hardcode source code URL and credits in about template, remove from DB/admin interface; only contacts remains editable
- Merge apropos editables into one À propos section, remove charte, add editable source code URL
- split jury into interne/externe/ULB,
- remove president from student form,
- add language_autre,
- split duration into pages+minutes+annexes,
- move licence to degrés d'ouverture with CC2r,
- add license_custom,
- filter PACS from student AP list,
- editable généralités help block,
- Libre toggle per settings
Fix:
- missing comma after cc4r column in schema.sql
- remove duplicate form footer from partage template
- remove couverture from student files fieldset; add promoteur ULB conditional disable via JS on Approfondi
- promoteur ULB: remove 'si applicable', make required when visible
- add exemplaire_baiu, exemplaire_erg, cc4r, remarks;
- add is_ulb to jury;
- split jury_lecteurs into interne/externe in view;
- refactor admin edit form with backoffice fields;
- update public fiche to show promoteur ULB and split lecteurs
- pad rows, distinguish empty year, better error diagnostics
- derive year from identifier when year column is empty
- fix remaining 18 theses: Installation/Performance (slash→dash) orientation alias
- csv importer: use column-name-based header detection instead of hardcoded positions
- shared repFilterEntry() and config array
- shared repFilterEntry() and $filterColumns config array
- fix single-valued FK fading via full intersection
- ThesisCreateController: comma-split auteurice, sort alphabetically,
use setThesisAuthors() instead of hardcoded createThesis() author_id
- Database::createThesis(): removed author_id param and hardcoded insert
- Database::findDuplicateThesis(): accepts array of author names, matches
any shared author via IN + DISTINCT
- ThesisEditController::save(): sort authors alphabetically on save
- File folder naming: slug from all authors alphabetically sorted
- v_theses_full GROUP_CONCAT: ORDER BY a.name ASC for deterministic display
- Migration 012_author_view_order.sql: rebuilds view with alphabetical order
Four ALTER TABLE / CREATE TABLE statements were applied locally but never
deployed to the remote production database, causing:
- acces.php → 500: share_links.is_archived missing (ShareLink::listActive/listArchived)
- parametres.php → 500: smtp_settings.notify_email missing (SmtpRelay::getSettings)
- /tfe?id=N → redirect-to-home: thesis_files.sort_order missing (getThesisFiles ORDER BY)
- admin_audit_log table missing (AdminLogger::insertDb, best-effort but noisy)
Adds four pending migrations (008–011) covering all missing schema changes.
Adds 'deploy-migrate' just recipe to run migrations on the remote after deploy.
- toast-fragment.php: 204 early-exit now also checks flash['warning'];
previously the warning was consumed by consumeFlash() then silently dropped
- partage/index.php: store warning as plain text; htmlspecialchars() applied
once at render time — previously htmlspecialchars() was called inside the
stored string then again at output, producing ' entities etc.
- partage/index.php: flash-warning div gets id + tabindex=-1; inline JS
scrolls it into view and focuses it on DOMContentLoaded
- admin/footer.php: htmx:afterSettle listener focuses .toast--warning after
HTMX injects the toast fragment into #toast-region
- Add DuplicateThesisException (typed, carries existing thesis metadata)
- Add Database::findDuplicateThesis(): matches on year + author + normalised
title (exact, prefix, Levenshtein ≤10% of longer string)
- ThesisCreateController::submit() runs duplicate check before any DB write
and throws DuplicateThesisException on match
- AppLogger::logDuplicate() writes status=duplicate entries to the JSON-lines
log for audit purposes
- App::flash/consumeFlash extended to support 'warning' flash type
- admin/actions/formulaire.php: catches DuplicateThesisException, logs it,
flashes an HTML warning toast with a clickable link to the existing thesis,
and repopulates the form fields
- partage/index.php: same catch block; surfaces a plain-text flash-warning
banner on the student form with identifier, title, and year of the match;
form is repopulated via session
- toast.php: renders toast--warning variant
- admin.css: .toast--warning + link colour rules
- form.css: .flash-warning style for the partage form
- 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`.
php -S (built-in dev server) ignores .htaccess and .user.ini entirely.
The POST Content-Length limit was still 8M from /etc/php/php.ini.
Pass upload_max_filesize=512M, post_max_size=520M, memory_limit=256M,
max_execution_time=300, max_input_time=300 directly on the CLI.