Commit Graph
100 Commits
Author SHA1 Message Date
Pontoporeia 37111eaac4 fix: add missing remote DB migrations and deploy-migrate recipe
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.
2026-05-05 11:04:52 +02:00
Pontoporeia ae6d9b86b3 Replace browser alert/confirm dialogs with <dialog> modals
- admin/index.php: alert() → no-selection dialog; confirm() bulk actions → bulk-confirm/bulk-delete dialogs; confirm() single delete → delete-thesis dialog; removed redundant confirm on Dépublier (reversible action)
- admin/tags.php: confirm() merge/delete → merge-tag/delete-tag dialogs
- admin/acces-etudiante.php: confirm() delete link → delete-link dialog
- admin/acces.php: confirm() archive link → archive-link dialog
- admin/parametres.php: confirm() maintenance/delete-all → enable-maintenance/delete-all-tfe dialogs; admin password confirm() kept with TODO comment
- admin/account.php: admin password confirm() kept with TODO comment
- admin.css: add .admin-dialog--sm, .admin-dialog__alert, .admin-dialog__footer styles
2026-05-05 11:04:52 +02:00
Pontoporeia ca5983075d feat: admin audit logging across all admin actions
- AdminLogger: JSON-lines → /var/log/xamxam.log (prod) / storage/logs/admin.log (dev)
  + best-effort DB mirror to admin_audit_log table
- DB: admin_audit_log table, share_links.is_archived column
- ShareLink: archive() replaces delete(), toggleActive() returns new state,
  listActive()/listArchived() split, validateLink blocks archived slugs
- All action handlers wired: publish, unpublish, visibility, delete, csv/db export,
  tfe add/edit, tags, pages, apropos, form-help, access-request, maintenance,
  settings (formulaire toggles, objet types, smtp update), smtp-test
- acces.php: archive button replaces delete; collapsible archived links section
- setup-server.sh: provision /var/log/xamxam.log (www-data:xamxam 640)
2026-05-05 11:04:52 +02:00
Pontoporeia 5f24dcae7e fix: duplicate warning not shown in admin, double-encoded in partage, no focus
- 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 &#039; 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
2026-05-05 11:04:52 +02:00
Pontoporeia a2cba6d3c0 feat: prevent duplicate TFE submissions with logging and user feedback
- 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
2026-05-05 11:04:52 +02:00
Pontoporeia 0a05f3911c Replace Psalm with PHPStan + PHP‑CS‑Fixer + Biome, add linting configs & cleanup
- 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`.
2026-05-05 11:04:52 +02:00
Pontoporeia d6e30ec9cd Enhance serve recipe to automatically open the browser
- use xdg-open firefox
- keep serve recipe in foreground
2026-05-05 11:04:52 +02:00
Pontoporeia 8a38708fc8 Refactor justfile to reduce redundancy and merge similar recipes
- Merge deploy-* recipes into a single deploy-script recipe
- Remove rarely used recipes (show id, setup-dirs)
- Simplify test-* recipes
- Remove redundant default recipe
- Preserve all critical functionality
2026-05-05 11:04:52 +02:00
Pontoporeia d09f1942f0 Fix Mistral provider: change api from openai-completions to mistral-conversations 2026-05-05 11:04:52 +02:00
Pontoporeia 34b2d073ee style(toast): reposition to bottom-center, solid bg, larger text, longer duration 2026-05-05 11:04:52 +02:00
Pontoporeia e8bf89d184 admin header: replace déconnexion text with SVG sign-out icon 2026-05-05 11:04:52 +02:00
Pontoporeia 0f849468f7 feat: inline email retry on 550 rejection in tfe access request form 2026-05-05 11:04:52 +02:00
Pontoporeia da53bf5d7a feat: email retry page on 550 rejection; confirmation_email optional in admin form 2026-05-05 11:04:52 +02:00
Pontoporeia 898a87789b fix(smtp-test): catch SmtpSendException to surface delivery errors as flash messages 2026-05-05 11:04:52 +02:00
Pontoporeia 19784090a3 fix: pass PHP upload limits via -d flags in justfile serve recipe
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.
2026-05-05 11:04:52 +02:00
Pontoporeia 6a37d21f3f docs: add file-uploads.md — accepted types, limits, storage, ordering, security 2026-05-05 11:04:52 +02:00
Pontoporeia a83dc1c74e feat: multi-type file upload with sort order, labels, and expanded MIME support
- DB migration 007: add sort_order + display_label to thesis_files
- Database: getThesisFiles ordered by sort_order; insertThesisFile accepts label/order;
  new reorderThesisFiles() and updateThesisFileLabel() methods
- ThesisCreateController + ThesisEditController: expand allowed MIME/exts to include
  audio (mp3/ogg/wav/flac/aac/m4a), video (webm/mov/ogv), image (gif/webp),
  archives (tar/gz), any-ext via octet-stream; max size raised to 500 MB;
  accept file_labels[] and file_orders[] POST fields; detectFileType() helper
- MediaController: expanded MIME allowlist; HTTP Range support for audio/video;
  force-download for unknown types; inline for known displayable types
- fieldset-files.php: sortable queue UI with SortableJS, per-file labels, 500 MB hint
- templates/admin/edit.php: existing files as sortable list with drag handles,
  type icons, label inputs, delete checkboxes, hidden sort-order fields
- file-upload-queue.js: new JS replacing file-preview.js — sortable new-file queue,
  per-file labels, hidden order fields on submit, backward-compat legacy preview
- tfe.php: renders audio (<audio>), all video formats, images, PDF, and
  download-only 'other' files; reads display_label; sorted by sort_order
- tfe.css + form.css: styles for audio player, download files, sortable queue,
  drag handles, file type badges, label inputs
- .htaccess + .user.ini: upload_max_filesize=512M / post_max_size=520M
2026-05-05 11:04:52 +02:00
Pontoporeia 2188ff5479 docs: add SMTP 550 postfix fix report for mail admin 2026-05-05 11:04:52 +02:00
Pontoporeia 89b7ab476e Handle SMTP 550 recipient-rejected errors with structured SmtpSendException
- Add SmtpSendException with smtpCode/smtpResponse/isRecipientRejected()
- smtpSend() $expect closure throws SmtpSendException (with code) instead of RuntimeException
- SmtpRelay::send() re-throws SmtpSendException so callers can inspect it
- request-access.php (new): catch 550 → roll back token+approval, return HTTP 422 with FR user message
- request-access.php (resend): catch 550 → HTTP 422 instead of silently claiming success
- StudentEmail::sendConfirmation(): catch SmtpSendException → log+false (submission not aborted)
- admin/actions/access-request.php: catch SmtpSendException post-approval → flash warning (recipient-rejected vs transient)
2026-05-05 11:04:52 +02:00
Pontoporeia 8d115dc965 smtp: enable TLS peer verification, fix envelope injection, fix dot-stuffing 2026-05-05 11:04:52 +02:00
Pontoporeia 33987c9b15 smtp: add notify_email field; fix admin notification sent to no-reply sender 2026-05-05 11:04:52 +02:00
Pontoporeia bdb68479d5 smtp: typed probe errors with per-field UI highlighting on save 2026-05-05 11:04:52 +02:00
Pontoporeia b750aca2f5 smtp: probe credentials on save (connect+auth+quit, no message sent) 2026-05-05 11:04:52 +02:00
Pontoporeia 56c8d54435 repertoire: align all column headings to shared baseline row 2026-05-05 11:04:52 +02:00
Pontoporeia a9e03c4b1c repertoire: fixed-header columns, remove main/index padding, minimal column padding 2026-05-05 11:04:52 +02:00
Pontoporeia 0960afb731 fix: add missing favicon tags to partage/recapitulatif.php 2026-05-05 11:04:52 +02:00
Pontoporeia 9ba60084bf fix: require SmtpRelay.php before StudentEmail.php in partage/index.php 2026-05-05 11:04:52 +02:00
Pontoporeia cb883ab33f fix: deploy-server.sh migrates posterg.db → xamxam.db and cleans legacy nginx configs 2026-05-05 11:04:52 +02:00
Pontoporeia ab51bf3a66 fix: deploy-server.sh cleans up legacy posterg configs and prunes old xamxam backups 2026-05-05 11:04:52 +02:00
Pontoporeia 68e30abb56 fix: remove Post-ERG branding → XAMXAM; drop legacy posterg nginx symlink in deploy script; rename posterg.db → xamxam.db 2026-05-05 11:04:52 +02:00
Pontoporeia c949cf9481 rename posterg → xamxam throughout: nginx conf, scripts, PHP source, docs 2026-05-05 11:04:52 +02:00
Pontoporeia 3e35bbc40f style: align mobile nav dropdown links left 2026-05-05 11:04:52 +02:00
Pontoporeia 471c892638 style: larger mobile nav dropdown links 2026-05-05 11:04:52 +02:00
Pontoporeia 42286b1b71 Header link modification 2026-05-05 11:04:44 +02:00
Pontoporeia 671cfb6d83 fix: hamburger dropdown not showing — reset display:none at mobile breakpoint 2026-04-30 00:02:44 +02:00
Pontoporeia 11f429eb72 feat: pure-CSS hamburger menu for public nav (≤640px) 2026-04-29 22:13:19 +02:00
Pontoporeia c27ffafa7e fix: add missing favicon tags to partage/index.php (error, password gate, form) 2026-04-29 21:58:49 +02:00
Pontoporeia 80b7fddea4 fix: partials must not unset caller-owned $formData
fieldset-academic.php, fieldset-metadata.php and fieldset-licence-explanation.php
were each calling unset($formData) (or wrong variable) in their cleanup block,
destroying the variable in the parent renderShareLinkForm() scope.  This caused
an Undefined variable / TypeError on old($formData, ...) for any field rendered
after those partials (e.g. confirmation_email at line 328).

Fix: remove $formData from the unset() calls; fieldset-licence-explanation.php
was also unsetting the wrong name — corrected to unset($n) which is the variable
it actually declares.
2026-04-29 21:56:42 +02:00
Pontoporeia 992f74b31c fix: prevent jury-fieldset partial from calling old() with wrong arity in partage context
Drop '?: null' coercions on juryPresident/juryPromoteur seeding in partage/index.php
so they are '' (not null), making the partial's $addMode guard false and skipping the
single-arg old() call that clashes with partage's 3-arg old() signature.
2026-04-29 21:56:42 +02:00
Pontoporeia 43702542eb feat(admin): sortable form-help blocks with two-panel UI
- Migration 005: add sort_order column to form_help_blocks
- Database: getAllFormHelpBlocks orders by sort_order; new reorderFormHelpBlocks()
- actions/form-help-reorder.php: HTMX POST handler, CSRF-validated, 204 response
- templates/admin/contenus.php: replace flat table with two-panel layout
  - Left: SortableJS 1.15.2 + htmx drag-and-drop ordered block cards
  - Right: static form structure reference showing fieldsets and their inputs
- admin.css: .fhb-* styles for layout, cards, ghost/chosen/drag states, anchors
- schema.sql: updated form_help_blocks DDL with sort_order column
2026-04-29 21:45:55 +02:00
Pontoporeia 5c39e856a3 fix: pass enabledAccessTypes from ThesisEditController to edit view 2026-04-29 21:34:47 +02:00
Pontoporeia 885150ea45 css: centralise semantic element baseline styles in common.css 2026-04-29 21:33:55 +02:00
Pontoporeia b5189c0d08 admin: merge acces-etudiante+file-access into acces.php, absorb system.php into parametres.php 2026-04-29 21:18:25 +02:00
Pontoporeia 670a38f30d add form help blocks: DB table, admin editor, live rendering in partage form 2026-04-29 21:08:09 +02:00
Pontoporeia 0437ec8d15 fix: escape apostrophe in FORM_HELP_LABELS string (Database.php:2005) 2026-04-29 21:05:53 +02:00
Pontoporeia d665cb502d centralise form fieldsets into shared partials; add TODO stubs in partage form 2026-04-29 20:59:35 +02:00
Pontoporeia 0628efbba3 Updated the README 2026-04-29 20:47:14 +02:00
Pontoporeia 89de6dd748 Removed the test csv 2026-04-28 22:21:35 +02:00
Pontoporeia 18a02a0018 deploy: rename deploy path from /var/www/posterg to /var/www/xamxam 2026-04-28 22:21:09 +02:00
Pontoporeia cd68e6e9d7 deploy: exclude posterg.db, theses/, covers/ from rsync to avoid overwriting remote data 2026-04-28 22:15:06 +02:00
Pontoporeia 59c4cf055f smtp-test: bypass DB, use POST fields directly for credentials 2026-04-27 21:44:10 +02:00
Pontoporeia 9ff8b1b464 fix: call RateLimit::checkKey() as instance method in request-access.php 2026-04-27 21:16:26 +02:00
Pontoporeia e09b056115 fix: iframe for PDF display, exclude cover files from public loop, no session on media requests 2026-04-27 21:11:58 +02:00
Pontoporeia 46a3c360ec fix: use local storage/ in dev, create upload dirs, gitignore uploads 2026-04-27 21:08:48 +02:00
Pontoporeia 48059c2317 fix: serve logs, formulaire.php error_log path, CSRF debug, undefined $redirect 2026-04-27 21:04:21 +02:00
Pontoporeia 32a7509598 feat: add file display to forms and recap pages
- Live file preview on all file inputs (file-field partial, edit template):
  thumbnails for images, emoji icons for PDF/video/zip/vtt, filename + size
- New file-preview.js wired via $extraJs in add.php / edit.php and direct
  <script> in partage/index.php; $extraJs support added to head.php
- admin/recapitulatif.php: replace plain table with rich file list — image
  thumbnails linked to media.php, type badges, human-readable size, date
- partage/recapitulatif.php: full rewrite — shows thesis metadata + files
  list with same rich display (no media links for student privacy)
- form.css: new sections for .file-preview-list (live preview) and
  .recap-file-list / .recap-dl / .partage-recap (recap pages)
2026-04-27 20:52:27 +02:00
Pontoporeia aca7e7eef8 rename thanks.php to recapitulatif.php in admin and partage 2026-04-27 20:41:43 +02:00
Pontoporeia 4d88bd8cc5 edit.php: rework Fichiers fieldset layout
- Drop file-field.php partial for cover/banner (it added a second label)
- Inline all three file inputs with admin-file-input wrapper
- Move banner inside the Fichiers fieldset
- Each entry: one label, one input, one small hint — no duplicate labels
- Context-aware hints: 'Laisser vide pour conserver...' when file already exists
2026-04-27 20:38:45 +02:00
Pontoporeia 8e864fc624 admin edit.php: add cover image + thesis file management fields
- Database: add deleteThesisFile() and handleCoverUpload() methods
- ThesisEditController::load(): expose currentFiles + currentCover to view
- ThesisEditController::save(): handle couverture upload/removal,
  per-file deletion (delete_files[]), and new thesis file uploads
- edit.php template: new Fichiers fieldset with cover preview+remove,
  existing files list with delete checkboxes, new file upload input
  (mirrors add.php / partage.php)
2026-04-27 20:33:21 +02:00
Pontoporeia 27e1b6828d Implement TFE file access restriction feature (complete)
Requirements:
- parametres.php toggle: 'restricted_files_enabled' enables/disables the feature
- Public TFE page: when enabled + access_type=Interne, hides files, shows French
  restriction message + access request form (metadata/synopsis still visible)
- ERG emails (@erg.school / @erg.be): auto-approve, send 24h access link immediately
- External emails: show justification textarea, create pending request, notify admin
- Admin panel /admin/file-access.php: approve/reject requests with optional notes,
  sends access email on approval (linked from admin nav with pending count badge)

Security:
- One-time 24h email tokens (used_at + is_valid=0 on first click)
- Token redeemed via POST /validate-access (GET shows confirmation page only)
- Long-lived 30-day browser session in file_access_sessions table
- Cookie: HttpOnly + Secure + SameSite=Strict
- CSRF on all mutations, rate limiting on request submission
- Audit trail: IP, UA, event, timestamp in file_access_audit

Bug fixes:
- admin/file-access.php: $vars never extract()ed → page was blank
- Template had self-contained head/footer includes (double-include)
- Admin approval URL used $requestId instead of $request['thesis_id']
- App::boot() now starts session so CSRF token works on public pages
- Dispatcher routes /validate-access and /request-access through front controller
2026-04-27 20:20:52 +02:00
Pontoporeia 54ef24d21f ignore *.db files, fix thesis identifier to use max seq instead of count, untrack .db files 2026-04-24 23:03:49 +02:00
Pontoporeia 4986fa74f4 add structured logging for admin/partage form submissions + migration system
- AppLogger: JSON-line logger in storage/logs/form-submissions.log
- Logs submissions (admin + partage) with IP, UA, thesis ID, author
- Logs errors with context (post keys, share slug)
- Migration runner (app/migrations/run.php) handles schema drift
- 001_add_objet_column.sql fixes production DB missing 'objet' column
- ThesisCreateController::getIdentifier() helper for logging
2026-04-24 23:03:49 +02:00
Pontoporeia decb9e2907 fix: replace mb_strlen/mb_substr in student-preview.php (mbstring unavailable) 2026-04-24 23:03:49 +02:00
Pontoporeia 9b4cb52617 fix: replace mb_strtolower with strtolower in admin import (mbstring unavailable in php8.4-fpm) 2026-04-24 23:03:49 +02:00
Pontoporeia 769beae4ee fix: drop hx-trigger once — rely on browser cache for dedup 2026-04-24 23:03:49 +02:00
Pontoporeia 743268cf1b fix: drop hx-trigger once — rely on browser cache for dedup 2026-04-24 23:03:49 +02:00
Pontoporeia 6eb111a6ab perf: htmx lazy popover with Cache-Control — no pre-render, images load on hover only 2026-04-24 23:03:49 +02:00
Pontoporeia e590d8e035 perf: pre-render student popover cards server-side into <template> tags — zero per-hover requests 2026-04-24 23:03:49 +02:00
Pontoporeia 53c3127140 feat: student name popover preview on /repertoire via htmx 2026-04-24 23:03:49 +02:00
Pontoporeia ede53746ba feat: student name popover preview on /repertoire via htmx 2026-04-24 23:03:49 +02:00
Pontoporeia d961f9533c feat: add objet field (tfe/thèse/frart) with share-link restriction and site-settings toggles 2026-04-24 23:03:49 +02:00
Pontoporeia dbaabaf8a0 merge all migrations into schema.sql 2026-04-24 23:03:49 +02:00
Pontoporeia 38031374c1 fix(partage): smtp view missing in schema + thanks redirect broken
- Add v_smtp_active VIEW to schema.sql (was only in migration 012,
  causing SmtpRelay::isConfigured() to always return false on fresh installs)
- Change thanks redirect from /partage/thanks.php to /partage/thanks
  (nginx 'location ~ \.php$ { deny all }' blocked the .php URL)
- Route /partage/thanks in index.php before slug validation
- Guard App::boot() in thanks.php to avoid double-boot when included
2026-04-24 23:03:49 +02:00
Pontoporeia 95bce2bbad Extract form CSS into form.css; scope system.css to system.php only 2026-04-24 23:03:49 +02:00
Pontoporeia d82556c596 Unify form CSS: move licence/share-badge styles to admin.css, remove inline style from partage form 2026-04-24 23:03:49 +02:00
Pontoporeia 057d2539eb SmtpRelay: parse EHLO caps, prefer AUTH PLAIN over AUTH LOGIN 2026-04-24 23:03:49 +02:00
Pontoporeia a3849a8e69 SmtpRelay: replace mail() stub with native socket SMTP client 2026-04-24 23:03:49 +02:00
Pontoporeia b448d0d40c Lock body scroll: only main/inner elements scroll on admin and public pages 2026-04-24 23:03:49 +02:00
Pontoporeia 5a58eefe66 feat(admin): add SMTP test email button on parametres page 2026-04-24 23:03:49 +02:00
Pontoporeia 4839b568de Separate admin views from controllers — move HTML to templates/admin/
All admin pages refactored to thin controllers + pure view templates, mirroring
the public-page pattern:

Controllers (public/admin/*.php): auth, data loading, include template
Views (templates/admin/*.php): pure HTML/PHP output
Fragment partials (templates/admin/partials/): toast, system-log-panel, system-nginx-config-panel

Pages migrated: login, tags, contenus, contenus-edit, account, acces-etudiante,
thanks, add, edit, parametres, system, index

Fragment endpoints refactored: system-fragment.php, toast-fragment.php
Skipped (pure redirects): logout, logs, status, import
2026-04-24 23:03:49 +02:00
Pontoporeia 362688c0fa fix: remove broken flash-messages include from admin footer; make repertoire columns scrollable 2026-04-24 23:03:49 +02:00
Pontoporeia 19ef2a11dc fix CSV importer AP/orientation name resolution + seed missing AP programs
- migration 014: adds Récits et expérimentation (RE), PACS, sets code NS
  on Narration Spéculative; applied to both posterg.db and test.db

- importer (admin/index.php): replaced the code-only ap_programs lookup
  (SELECT WHERE code=?) and the orientationMap short-code translation with
  two resolver closures that handle the real CSV format (full names):

  resolveAP(): alias map for L.I.E.N.S., case variants → exact name
  match → code match (legacy) → case-insensitive name match

  resolveOrientation(): legacy 2-letter code map → alias map for
  Installation/Performance, Arts numériques, Design numérique →
  exact name match → case-insensitive name match

  All 5 AP values and 13 orientation values from the real CSV now
  resolve to correct DB IDs. Legacy short-code CSVs (test.db format)
  continue to work unchanged.
2026-04-24 23:03:49 +02:00
Pontoporeia 73fdda4a7f fix repertoire AP/OR/FI columns and main scroll containment
- repertoire-index.php: add $colHasMatches per-column guard.
  Entries in a column are only faded when that column has at least one
  matched entry in the current result set. When a dimension has no
  matched entries (e.g. no thesis has orientation_id set yet), the
  entire column stays fully interactive — all values remain clickable.
  This fixes: empty columns, forced single-select, cascade fading.

- Database.php: revert allAp/allOr/allFi to full lookup-table queries
  so all known values are always shown (not just ones linked to theses).

- common.css: body is now a flex column; main gets flex:1 + min-height:0;
  header-search-wrap gets flex-shrink:0; duplicate html/body blocks merged.
- public.css: removed redundant top-level main block; home-main gets min-height:0.
- repertoire.css: search-main gets min-height:0 for proper flex scroll.
2026-04-24 23:03:49 +02:00
Pontoporeia 78449afe64 some css changes 2026-04-24 23:01:25 +02:00
Pontoporeia 1b02ccb1d5 fix: mark languages as required, add required-field visual indicators on both forms
- checkbox-list.php: support $required prop → adds required + aria-required on fieldset
- add.php: languages checkbox now marked required (matches server-side validation)
- partage/index.php: same for student form
- admin.css: dashed border on required inputs, bold labels, red asterisk via :has(), "Champs obligatoires" note
- Both forms now show "* Champs obligatoires" note at top

Server-side required fields = titre, auteurice, synopsis, année, orientation, ap, finality, languages (≥1), access_type_id, confirmation_email. All now have required attribute + visual asterisk.
2026-04-20 16:19:55 +02:00
Pontoporeia e21a4d81a2 refine: required confirmation_email field on both student forms, StudentEmail uses it directly
- Add dedicated 'confirmation_email' (type=email, required) field
  to student form at end of submission (partage + admin).
- ThesisCreateController now validates it is present and a valid
  email; form is rejected if missing/invalid.
- Autofocus mapping for confirmation_email errors.
- StudentEmail uses confirmation_email directly (removed extractEmail
  hack that mined email from free-form contact field).
2026-04-20 15:47:55 +02:00
Pontoporeia fa75ca4a65 fix: inline getDatabasePath into Database.php, delete config/config.php
- Remove require_once for config/config.php (file was never deployed — outside app/)
- Inline DB path resolution directly in Database::determineDatabasePath()
- Uses APP_ROOT when defined (bootstrap already loaded), falls back to __DIR__/../
- DB_ENV=test|prod env-var override preserved for tests
- php -S cli-server -> test.db, nginx/fpm -> posterg.db
2026-04-20 14:23:30 +02:00
Pontoporeia 468278349a fix: router.php — bypass admin requests to front controller 2026-04-20 14:13:44 +02:00
Pontoporeia 033599aa36 fix: config/config.php — correct DB paths to app/storage/, add test.db for dev 2026-04-20 14:12:26 +02:00
Pontoporeia 5af31aceda fix: Database.php require_once -> resolve config.php from app/src/ 2026-04-20 14:11:16 +02:00
Pontoporeia de2e7a61ee feat: single entry point routing — convert to front controller pattern
- Create app/public/index.php as front controller (bootstrap + Dispatcher)
- Rewrite app/router.php for PHP dev server → all non-asset requests to index.php
- Update Dispatcher to render full page layouts (head+header+view+footer)
- Move public view templates into templates/public/ (home, search, tfe, about, repertoire)
- Delete dead direct-access public/*.php files (apropos, search, tfe, licence, repertoire)
- Add clean URL routes to Dispatcher (/search, /tfe, /repertoire, /apropos, /licence, /media)
- Remove .php extensions from all internal links (header, views, templates, URLs)
- Update OG tags in controllers to use clean URLs
- Update nginx posterg.conf → front-controller try_files pattern, block direct .php access
- Update header.php and search-bar.php form actions to clean URLs
- Switch AboutController nav key from 'nav' to 'currentNav' for consistency
2026-04-20 12:42:15 +02:00
Pontoporeia 75f808bee4 feat: extract MediaController, wire into Dispatcher, delete media.php 2026-04-20 12:32:00 +02:00
Pontoporeia b03be51b92 feat: migrate admin system page to HTMX with tab-based navigation and log viewer 2026-04-16 15:03:40 +02:00
Pontoporeia 05002ccee4 fix: allow isAuthenticated() bypass in development mode 2026-04-16 15:03:33 +02:00
Pontoporeia bf30aab0b3 migrate apropos data from config/apropos.php to SQLite
- Create apropos_contents table via migration 010
- Add Database methods: getAproposContent(), saveAproposContent(), getAllAproposContents()
- Replace admin/pages.php with admin/contenus.php (renamed header from 'Pages statiques' to 'Contenus')
- Replace admin/pages-edit.php with admin/contenus-edit.php (support editing pages + apropos contents)
- Create admin/actions/apropos.php for saving apropos data (contacts, credits, erg_url)
- Update public/apropos.php to read contacts/credits/erg_url from DB
- Delete config/apropos.php
2026-04-16 14:22:41 +02:00
Pontoporeia 4158c72d08 admin: replace header 'Ajouter un TFE' nav link with toolbar button 2026-04-16 13:07:16 +02:00
Pontoporeia e70a65ffb6 fix: session boot on POST path, consolidate rate limiter via checkKey() 2026-04-16 13:07:16 +02:00
Pontoporeia a6df3c8c0e fix: /partage/<slug> routing (regex delimiter + nginx location) 2026-04-16 13:07:16 +02:00
Pontoporeia b7be93e30b Security: rate limiting and flash messaging for partage share links
- Add rate limiting (5 submissions per IP per 10 min, per share link)
  to prevent abuse of shared submission endpoints
- Replace all plain die() error responses with styled flash messages
  and redirects (invalid slug, disabled link, expired link, wrong password,
  rate limit exceeded, CSRF failure)
- Add dedicated error page renderer for disabled/expired links with
  home page link
- Password gate now uses flash message via session redirect instead
  of inline error variable
2026-04-16 13:07:16 +02:00
Pontoporeia 150099dc3c admin: replace header 'Ajouter un TFE' nav link with toolbar button 2026-04-16 13:07:16 +02:00