Commit Graph

37 Commits

Author SHA1 Message Date
Pontoporeia
f4a3e26901 Add thesis status column for two-phase commit lifecycle tracking 2026-06-11 12:09:43 +02:00
Pontoporeia
021c58925e fix: auto-regenerate thesis identifier on any year-prefix mismatch, support .php migrations in runner
ThesisEditController::save() previously only regenerated the identifier when
the year field changed during an edit. If a thesis had its year corrected in
a past edit (or via other means) and the identifier still carried the old
year prefix, subsequent edits that didn't touch the year field would leave
the mismatched identifier in place.

Now saves() also checks whether the existing identifier's 4-digit prefix
matches the thesis year, and regenerates if not — regardless of whether year
changed in the current edit.

The migration runner (run.php) only scanned for .sql files, so PHP migrations
(013, 016, 018, 038) were never auto-applied. Extended the runner to also
discover and execute .php migrations in a subprocess. If a PHP migration fails
with an idempotent error (no such column, already exists, duplicate column),
the runner treats it as already-applied and continues rather than aborting
— preventing a stale migration like 016 (banner_path already dropped by 028)
from blocking migrations that come after it alphabetically (e.g. 038).

Updated migrations 016 and 038 to accept an optional $argv[1] DB path.
Fixed 016 to gracefully handle the banner_path column already being gone
(exit 0 instead of fatal).
2026-06-10 00:17:30 +02:00
Pontoporeia
34739d6ae5 feat: migration 038 to fix thesis identifiers mismatched with their year 2026-06-10 00:17:00 +02:00
Pontoporeia
3016c199bd Fix edit form: is_published reset, contact decoupling, note label, author name case
- Fix #1: Add is_published to getThesisRawFields() SELECT so the publish
  checkbox stays checked when editing an already-published TFE.
- Fix #2: Rename 'Note contextuelle' → 'Note contextuelle relative à
  soutenance' in all templates and StudentEmail.
- Fix #3: Update findOrCreateAuthor to also UPDATE the author name when
  a record is found by name (fixes inability to capitalise names).
- Fix #4/#5: Decouple contact_interne (private author email) from
  contact_visible (public contact on TFE page). Add migration 037 to
  add contact_visible TEXT column to theses table and rebuild
  v_theses_full view. Update all controllers, templates, and DB methods
  to treat them independently.
- Fix #6: Investigated libre→interne restriction — no code barrier
  found; likely resolved by is_published fix.
2026-06-10 00:17:00 +02:00
Pontoporeia
053f09b181 fix(migration): deduplicate languages before LOWER() in 025_lowercase_languages.sql
Two rows (Néerlandais id=5, néerlandais id=3) collided when lowercased,
violating the UNIQUE constraint on languages.name.

Added DELETE to keep the lowest-ID row per LOWER(name) group before
the UPDATE SET name = LOWER(name).
2026-06-08 10:17:00 +02:00
Pontoporeia
9152b120e8 feat: mandatory auto-generated passwords for share links + admin password copy/regeneration + password gate rate limiting 2026-05-19 00:08:06 +02:00
Pontoporeia
8bb0b3a1f2 refactor: unify FilePond edit previews + clean upload UI and shared fragments
* Move shared `fichiers-fragment.php` from `partage/` to `templates/partials/form/`
  and update all include/require references
* `.gitignore`: exclude SQLite WAL/SHM journal files
* FilePond UI:

  * change uploaded file block border state from yellow to green
  * restyle image previews to use site light-theme colors
* Edit mode:

  * remove custom existing-file preview list implementation
  * preload existing files directly into FilePond pools
  * include `cover` and `note_intention` assets in FilePond-managed state
* Remove obsolete upload progress bar UI and related JS includes
* Remove deprecated `Écriture` + `Image` format types from upload flow/configuration
2026-05-19 00:08:06 +02:00
Pontoporeia
df12af8423 fix: validation error messages hidden by generic fallback in ErrorHandler::userMessage
ErrorHandler::userMessage only handled RuntimeException, but all validation
throws in ThesisCreateController and ThesisEditController use plain Exception.
This caused user-friendly messages like 'Le champ Nom/Prénom/Pseudo est requis'
to fall through to the 'Une erreur inattendue est survenue…' generic message.

Fix: add Exception check (after PDOException, since PDOException extends it)
so all validation exceptions pass their message through.
2026-05-19 00:08:06 +02:00
Pontoporeia
d873a7f09e fix: add upload-progress.js to partage form (progress bar was missing on public submissions) 2026-05-19 00:08:06 +02:00
Pontoporeia
3ae22cd427 add system dependency checks (php-curl, sqlite3) to deploy-server.sh step 0 2026-05-19 00:08:06 +02:00
Pontoporeia
cb6394e119 add incremental migration runner to deploy recipe — execute whole SQL files (not semicolon-split), catch 'no such column' for idempotent re-runs, merge into migrate.sh 2026-05-19 00:08:06 +02:00
Pontoporeia
83a5a508ea feat: PeerTube integration — alternate audio/video labels, FilePond pools, shared SMTP credentials, channel by name, test button, resumable upload, embed improvements, fix alt labels/curl_close/deprecation 2026-05-19 00:08:06 +02:00
Pontoporeia
926659087f feat: implement SQLite backup & data integrity plan (Phases 2-4) 2026-05-19 00:08:06 +02:00
Pontoporeia
8db7b6e9eb feat: FilePond production hardening — extension-based validation, server-side size limits (2GB), annexe validation, drop accept attributes, FilePond file styling 2026-05-19 00:08:05 +02:00
Pontoporeia
ab6e266807 fix: add help email, preserve file names on validation error, license fix
The share link (partage) form does not expose a license field and does
not send access_type_id (defaults to 2/Interne). Server-side validation
was unconditionally requiring a license for non-admin submissions,
causing all share link submissions to fail.

Now the license check is gated on adminMode=false AND accessTypeId=1
(Libre), matching the client-side HTMX fragment behaviour in
licence-fragment.php. Also fixed a use-before-definition where
accessTypeId was referenced before being assigned.

Student form improvements:
- Add xamxam@erg.be mailto link at top of form
- On validation error, append "Si le problème persiste, envoyez un
  e-mail à xamxam@erg.be" to the flash message
- Preserve uploaded file names across validation redirects: store in
  session (share_primed_files_<slug>), display as warning on form
  re-render so the student knows which files to re-select

- License: only required for non-admin when access_type_id=1 (Libre),
  not for Interne (2) or Interdit (3). Fixes share link submissions
  failing with "Veuillez sélectionner une licence". Also fixed
  use-before-definition of accessTypeId.
2026-05-19 00:08:05 +02:00
Pontoporeia
96fa8ee266 CSV importer: boolean and ap variants/typos
- add AP aliases for:
  - Design & politique du multiple → DPM,
  - Pratiques artistiques & complexité scientifique → PACS,
  - Narraion Speculative typo → NS
- Fix: OUI/NON CSV artefacts in contact_interne — clean DB, guard in findOrCreateAuthor and CSV import
- Cleaned 141 authors.email = 'NON' rows → NULL in dev DB
- findOrCreateAuthor: treat OUI/NON as null (CSV boolean artefact in email column)
- CSV import: sanitize contact column — OUI/NON → empty string before passing to findOrCreateAuthor
2026-05-19 00:08:05 +02:00
Pontoporeia
a80b2c08bf Admin mobile block: fix inline style beating media query 2026-05-19 00:08:05 +02:00
Pontoporeia
c4a23d5c2d Remove duration_pages/duration_minutes/file_size_info; rename cc4r → cc2r in DB and code 2026-05-19 00:08:05 +02:00
Pontoporeia
cc0ae32df0 fix: resolve partage form submission issues
- 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.
2026-05-19 00:08:05 +02:00
Pontoporeia
21c2b55bfb style: normalize headers, overtype editor rounded corners, remove duplicate cover preview, thesis-add-header grid layout, subtitle below header with top gradient 2026-05-19 00:08:05 +02:00
Pontoporeia
03c5fd217e feat: dual upload system — direct file storage + PeerTube API integration
Adds a parallel PeerTube upload system behind a feature flag (disabled by default
until upload quota is granted). When disabled, the existing direct file upload
path works unchanged.

Files:
- src/PeerTubeService.php — credential storage (encrypted), OAuth2 token
  retrieval, multipart upload to /api/v1/videos/upload
- migrations/021_peertube_settings.sql — peertube_settings singleton table
  + peertube_upload_enabled site_setting (default 0)
- admin/actions/settings.php — peertube section handler
- admin/parametres.php / templates/admin/parametres.php — PeerTube UI section
- partage/fichiers-fragment.php — shows file inputs when enabled, TODO notice otherwise
- ThesisCreateController / ThesisEditController — handlePeerTubeUpload()
- tfe.php — PeerTube iframe embed detection
- AdminLogger — logPeerTubeUpdate()
2026-05-13 17:59:13 +02:00
Pontoporeia
e6829994b6 Refactor + feat: unify format/fichiers HTMX fragment, reorder format types, add file constraints, fix admin auth
* **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.
2026-05-13 17:59:13 +02:00
Pontoporeia
7e35bba530 Encrypt SMTP password at rest with AES-256-GCM 2026-05-13 17:59:13 +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
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
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
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
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
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
33987c9b15 smtp: add notify_email field; fix admin notification sent to no-reply sender 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
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
0437ec8d15 fix: escape apostrophe in FORM_HELP_LABELS string (Database.php:2005) 2026-04-29 21:05:53 +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
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