# XAMXAM TODO ## Duplicate TFE submission prevention (fixes) - [x] `DuplicateThesisException` — typed exception carrying existing thesis metadata - [x] `Database::findDuplicateThesis()` — year + author + normalised-title matching (exact, prefix, Levenshtein ≤10%) - [x] `ThesisCreateController::submit()` — calls duplicate check before any DB write, throws `DuplicateThesisException` - [x] `AppLogger::logDuplicate()` — dedicated log action (`status: duplicate`) for audit trail - [x] `App::flash/consumeFlash` — extended to support `warning` type alongside `error`/`success` - [x] `admin/actions/formulaire.php` — catches `DuplicateThesisException` separately; logs it; flashes HTML warning with link to existing thesis; repopulates form - [x] `partage/index.php` — same catch block; plain-text warning (no admin link) surfaced on the student form via `flash-warning` banner; form repopulated - [x] `toast.php` — renders `toast--warning` block - [x] `admin.css` — `.toast--warning` style + link colour - [x] `form.css` — `.flash-warning` style (partage form) ## Admin audit logging - [x] `AdminLogger` class — JSON-lines to `/var/log/xamxam.log` (prod) or `storage/logs/admin.log` (dev), mirrors to `admin_audit_log` DB table - [x] `admin_audit_log` DB table — created in schema + migrated - [x] `share_links.is_archived` column — archive replaces delete; stats preserved - [x] `ShareLink::archive()` — new method; `toggleActive` returns new state; `listActive()` / `listArchived()` split; `validateLink` blocks archived slugs - [x] `actions/acces-etudiante.php` — delete→archive, all actions logged (create, toggle, set_password, archive) - [x] `actions/publish.php` — publish/unpublish logged - [x] `actions/delete.php` — delete / bulk-delete / delete-all logged - [x] `actions/visibility.php` — visibility changes logged - [x] `actions/export-csv.php` — CSV export logged - [x] `actions/export-db.php` — DB export logged - [x] `actions/edit.php` — TFE edit logged - [x] `actions/formulaire.php` — TFE add from admin logged - [x] `actions/tag.php` — rename/merge/delete logged - [x] `actions/page.php` — static page edits logged - [x] `actions/apropos.php` — à-propos edits logged - [x] `actions/form-help.php` — form structure edits logged - [x] `actions/access-request.php` — approve/reject logged - [x] `actions/maintenance.php` — maintenance on/off logged - [x] `actions/settings.php` — formulaire toggles, objet types, SMTP update logged - [x] `actions/smtp-test.php` — SMTP test logged - [x] `templates/admin/acces.php` — archive button, archived links collapsible section - [x] `scripts/setup-server.sh` — provision `/var/log/xamxam.log` with correct ownership ## Multi-author support - [x] `ThesisCreateController::validateAndSanitise()` — comma-split `auteurice`, sort alphabetically, build author entries array - [x] `Database::createThesis()` — removed hardcoded `author_id` insert; authors linked via `setThesisAuthors()` instead - [x] `ThesisEditController::save()` — authors sorted alphabetically before `setThesisAuthors()` - [x] `Database::findDuplicateThesis()` — accepts `array` of author names, matches any shared author via `IN` + `DISTINCT` - [x] File folder naming — slug generated from all authors alphabetically sorted (both create and edit) - [x] `v_theses_full` GROUP_CONCAT — `ORDER BY a.name ASC` for deterministic alphabetical display - [x] Migration `012_author_view_order.sql` — rebuilds view with alphabetical author ordering ## Fix remote 500s and broken TFE pages (post-deploy) - [x] `migrations/pending/008_share_links_is_archived.sql` — `ALTER TABLE share_links ADD COLUMN is_archived` (missing on remote; breaks `acces.php`) - [x] `migrations/pending/009_admin_audit_log.sql` — `CREATE TABLE admin_audit_log` (missing on remote) - [x] `migrations/pending/010_smtp_notify_email.sql` — `ALTER TABLE smtp_settings ADD COLUMN notify_email` (missing on remote; breaks `parametres.php` via `SmtpRelay::getSettings()`) - [x] `migrations/pending/011_thesis_files_sort_and_label.sql` — `ALTER TABLE thesis_files ADD COLUMN sort_order / display_label` (missing on remote; breaks every public TFE detail page) - [x] `justfile` — added `deploy-migrate` recipe: SSHes to remote and runs `php migrations/run.php` ## Replace browser dialogs with `` modals - [x] `admin/index.php` — `alert()` (no selection) → ``; `confirm()` bulk publish/unpublish → ``; `confirm()` bulk delete → ``; `confirm()` single delete → ``; inline `confirm()` on Dépublier button removed (no confirmation needed for reversible action) - [x] `admin/tags.php` — `confirm()` merge → ``; `confirm()` delete → `` - [x] `admin/acces-etudiante.php` — `confirm()` delete link → `` - [x] `admin/acces.php` — `confirm()` archive link → `` - [x] `admin/parametres.php` — `confirm()` enable maintenance → ``; `confirm()` delete all TFE → ``; admin password `confirm()` kept with `TODO` comment - [x] `admin/account.php` — admin password `confirm()` kept with `TODO` comment - [x] `admin.css` — added `.admin-dialog--sm`, `.admin-dialog__alert`, `.admin-dialog__footer` styles ## Fix 403 on HTMX tab requests in parametres.php - [x] `AdminAuth::requireLogin()` — now sets `$_SESSION[SESSION_KEY]` when accepting nginx Basic Auth credentials (was returning early without marking the session) - [x] `AdminAuth::isAuthenticated()` — now falls back to `PHP_AUTH_PW` verification (same logic as `requireLogin`) so HTMX requests to `system-fragment.php` authenticate even before a session exists ## Duplicate warning display fixes - [x] `toast-fragment.php` — 204 guard now also checks `warning`; warning was silently discarded before - [x] `partage/index.php` — warning stored as plain text (no pre-escaping); `htmlspecialchars()` applied once at render; was double-encoded before - [x] `partage/index.php` — `flash-warning` div gets `id` + `tabindex=-1`; inline JS scrolls and focuses it on load - [x] `admin/footer.php` — `htmx:afterSettle` listener focuses `.toast--warning` after HTMX injects the toast fragment