Commit Graph
100 Commits
Author SHA1 Message Date
Pontoporeia 5735ccbc38 Fix issues with nginx access to pages
- 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.
2026-05-13 17:58:29 +02:00
Pontoporeia 6ba13e00ea test: add ShareLinkTest + PureLogicTest (TDD), fix coverMap undefined in SearchController 2026-05-08 22:58:25 +02:00
Pontoporeia 15d54fa19e add Néerlandais language option and make language_autre conditionally required 2026-05-08 22:58:25 +02:00
Pontoporeia f3d9615562 merge banners into covers: remove banner field, migrate files, add covers to search/home/repertoire cards 2026-05-08 22:58:25 +02:00
Pontoporeia e3896811c4 Fix migrations and deploy issues + errors + linting
- scan both pending/ and applied/ dirs so remote catch-up works
- fix remote 500s: run.php handles per-statement errors so VIEW rebuilds run after duplicate columns; replace mb_strimwidth with substr (no mbstring extension on server)
- add missing migration: 015_license_custom.sql (column existed in schema.sql but was never migrated)
- remote: fgetcsv enclosure single-char + AdminLogger permission-denied
guard + deploy always migrates
- fix admin-filters wrapping: restore flex-wrap, flex-basis on
inputs/selects, shrink-protect buttons
- fix phpstan: remove redundant ?? [] after isset guard in
ThesisEditController
- biome: exclude vendored min.js via includes patterns;
lint whole js dir; modernise beforeunload-guard.js
2026-05-08 22:58:05 +02:00
Pontoporeia bdd95341b0 Extract shared TFE form partial — single source of truth for add/edit/partage
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.
2026-05-07 23:39:41 +02:00
Pontoporeia ac0008df6c Add website-type TFE support: URLs stored as thesis_files rows, HTMX-toggle on Site web format 2026-05-07 23:39:41 +02:00
Pontoporeia 9dc7ea98f2 fix: password-protected share links never load form after password entry
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.
2026-05-07 23:39:41 +02:00
Pontoporeia 03121d6b7e form: add spacing between elements inside fieldsets
- common.css: fieldset > *:not(:last-child) gets margin-bottom: var(--space-xs)
- admin.css: .param-form fieldset > * zeroes margin to avoid double-spacing with flex gap
2026-05-07 23:39:41 +02:00
Pontoporeia d9dd4bdbc7 Edit Email confirmation styling 2026-05-07 23:39:41 +02:00
Pontoporeia 696259afae Fix form field required states & missing fields per spec
- Admin add: add contact_public checkbox (matching edit form)
- All forms: formats checkbox-list now required
- All forms: jury promoteur·ice interne required, lecteur·ice interne/externe required
- All forms: licence select now required
- Admin edit: add E-mail de confirmation fieldset
- Partage: contact always visible when provided (no contact_public field)
- Partage: filter PACS from AP programs dropdown
- Server-side validation: formats, jury, licence required (create + edit controllers)
- Autofocus mappings for new validation errors
- No duplicate asterisks — verified across all rendered fields
- fix: add missing old() function in admin edit controller
- refactor: move admin email field to Backoffice as Contact interne, never send email
- Untrack admin.log (covered by .gitignore)
2026-05-07 23:39:41 +02:00
Pontoporeia 51f9f56e09 Replace span with a link + href in about.php for credits 2026-05-07 23:39:41 +02:00
Pontoporeia e0c748d8e7 Refactor about.php
- 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
2026-05-07 19:44:18 +02:00
Pontoporeia 24d68dda59 refactor form structure per new spec + fix
- 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
2026-05-07 19:43:43 +02:00
Pontoporeia dce0e0b301 schema: validate against new TFE field spec
- 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
2026-05-07 17:53:24 +02:00
Pontoporeia 7793b6f86d add file export system for admins
- ExportController: getAllThesisFiles(), buildExportManifest(), createExportZip()
  builds a ZIP archive with manifest.json + files/ mirror of storage/theses/
- Database: getAllThesisFilesForExport() queries all thesis_files + identifier
- AdminLogger: logFilesExport() audit log entry
- admin/actions/export-files.php: thin dispatcher, streams zip with headers
- templates/admin/index.php: 'Exporter fichiers' button next to CSV export
2026-05-07 16:40:16 +02:00
Pontoporeia 821369f004 exclude maintenance.flag from rsync deploy and git 2026-05-07 12:35:31 +02:00
Pontoporeia 3f87d71e38 Fix: CSV importer and imported data
- 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
2026-05-07 12:35:31 +02:00
Pontoporeia b063312642 centralise repertoire filter column rendering
- shared repFilterEntry() and  config array
- shared repFilterEntry() and $filterColumns config array
- fix single-valued FK fading via full intersection
2026-05-07 12:35:25 +02:00
Pontoporeia bca707ee96 standardise buttons: .btn base class (border-radius 10px, padding var(--space-xs)) 2026-05-05 11:59:43 +02:00
Pontoporeia b58445f71c sticky save/cancel buttons at top-right of admin edit page 2026-05-05 11:22:19 +02:00
Pontoporeia 95066de7b4 standardise multi-author support across all forms
- 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
2026-05-05 11:04:52 +02:00
Pontoporeia 125c501f40 Fix 403 on HTMX fragment requests: AdminAuth Basic Auth sets session key 2026-05-05 11:04:52 +02:00
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