diff --git a/TODO.md b/TODO.md index 6e47ecb..02ee247 100644 --- a/TODO.md +++ b/TODO.md @@ -11,6 +11,8 @@ Pending tasks have been split into topic files under [`todo/`](todo/README.md): ## Recently completed (this session) +- [x] WCAG 3.3.1 `autofocus` on first invalid field — `App::flashAutofocus()` / `consumeAutofocus()` added; `actions/formulaire.php` maps exception messages → field names and stores the autofocus hint in `$_SESSION['_flash_autofocus']`; `actions/edit.php` does the same; `add.php` consumes it via a `withAutofocus()` helper and injects `autofocus => true` into `$attrs` for `text-field.php` / `select-field.php` includes; `edit.php` uses inline ternary for the same; `text-field.php` and `select-field.php` partials now support boolean `true` values in `$attrs` (emit bare attribute names for `autofocus`, `required`, etc.) + - [x] `config/apropos.php` — extracted hardcoded contacts (Laurent Leprince, Xavier Gorgol, Brigitte Ledune) and credits into a config array (`contacts[]`, `credits[]`, `erg_url`); `public/apropos.php` now loops over the config with `htmlspecialchars` instead of embedding names/emails in HTML - [x] `todo/02-php-components.md` — audited and marked 8 stale items as already done: all 5 form field partials (`text-field`, `select-field`, `checkbox-list`, `file-field`, `jury-fieldset`), `admin-alert.php`/`flash-messages.php` consolidation, `RateLimit` cache dir placement, and `apropos.php` contacts extraction diff --git a/public/admin/actions/edit.php b/public/admin/actions/edit.php index 5df8d5f..d74af41 100644 --- a/public/admin/actions/edit.php +++ b/public/admin/actions/edit.php @@ -136,6 +136,18 @@ try { error_log("Edit action error: " . $e->getMessage()); App::flash('error', $e->getMessage()); + + // WCAG 3.3.1 — map error to the field that caused it so the form can autofocus it. + $msg = $e->getMessage(); + $autofocusField = null; + if (str_contains($msg, 'titre') || str_contains($msg, 'Titre')) $autofocusField = 'titre'; + elseif (str_contains($msg, 'année') || str_contains($msg, 'année')) $autofocusField = 'année'; + elseif (str_contains($msg, 'synopsis') || str_contains($msg, 'Synopsis')) $autofocusField = 'synopsis'; + elseif (str_contains($msg, 'auteur') || str_contains($msg, 'Auteur')) $autofocusField = 'auteurice'; + if ($autofocusField !== null) { + App::flashAutofocus($autofocusField); + } + header('Location: ../edit.php?id=' . $thesisId); exit(); } diff --git a/public/admin/actions/formulaire.php b/public/admin/actions/formulaire.php index 9343544..e9bcf57 100644 --- a/public/admin/actions/formulaire.php +++ b/public/admin/actions/formulaire.php @@ -322,6 +322,24 @@ try { App::flash('error', $e->getMessage()); $_SESSION['form_data'] = $_POST; + // WCAG 3.3.1 — identify which field caused the error and request autofocus. + // Mapping is based on the exception messages thrown above. + $msg = $e->getMessage(); + $autofocusField = null; + if (str_contains($msg, "Nom/Prénom/Pseudo")) $autofocusField = 'auteurice'; + elseif (str_contains($msg, "Titre du mémoire")) $autofocusField = 'titre'; + elseif (str_contains($msg, "Synopsis")) $autofocusField = 'synopsis'; + elseif (str_contains($msg, "Année invalide")) $autofocusField = 'année'; + elseif (str_contains($msg, "orientation")) $autofocusField = 'orientation'; + elseif (str_contains($msg, "Atelier Pratique")) $autofocusField = 'ap'; + elseif (str_contains($msg, "finalité")) $autofocusField = 'finality'; + elseif (str_contains($msg, "langue")) $autofocusField = 'languages'; + elseif (str_contains($msg, "mots-clés")) $autofocusField = 'tag'; + elseif (str_contains($msg, "Lien URL")) $autofocusField = 'lien'; + if ($autofocusField !== null) { + App::flashAutofocus($autofocusField); + } + // Redirect back to form with preserved data header('Location: ../add.php'); exit(); diff --git a/public/admin/add.php b/public/admin/add.php index aaed1f6..a2ca581 100644 --- a/public/admin/add.php +++ b/public/admin/add.php @@ -24,10 +24,23 @@ try { die("Erreur lors du chargement du formulaire."); } -$formData = $_SESSION["form_data"] ?? []; -unset($_SESSION["form_data"]); +$formData = $_SESSION['form_data'] ?? []; +unset($_SESSION['form_data']); +$autofocusField = App::consumeAutofocus(); // Flash error consumed by the flash-messages partial below. +/** + * Merge autofocus into the $attrs array for a given field. + * Only adds the attribute when $autofocusField matches $fieldName. + */ +function withAutofocus(string $fieldName, array $attrs = []): array { + global $autofocusField; + if ($autofocusField === $fieldName) { + $attrs['autofocus'] = true; + } + return $attrs; +} + function old($key, $default = "") { global $formData; return isset($formData[$key]) ? htmlspecialchars($formData[$key]) : $default; @@ -50,9 +63,9 @@ function wasSelected($key, $value) {