# TODO Pending tasks have been split into topic files under [`todo/`](todo/README.md): | File | Topic | |------|-------| | [todo/01-css-semantic-refactor.md](todo/01-css-semantic-refactor.md) | CSS class audit, semantic HTML (public + admin), inline style extraction, favicon | | [todo/02-php-components.md](todo/02-php-components.md) | Form field partials, shared UI partials, controller extraction, backend maintenance | | [todo/03-system-cache.md](todo/03-system-cache.md) | `system_cache` table, `SystemCache` class, `system.php` refactor | | [todo/04-accessibility.md](todo/04-accessibility.md) | WCAG 2.1 AA - remaining failures grouped by success criterion | ## Recently completed (this session) - [x] `src/ThesisEditController.php` — extracted all data-fetching and mutation logic from `admin/edit.php` and `admin/actions/edit.php` into a dedicated controller class; `load(int $thesisId): array` fetches the thesis row, current language/format/jury selections, and all lookup tables for the view; `save(int $thesisId, array $post, array $files): void` validates and persists thesis metadata, authors, jury, languages, formats, tags, and banner in a transaction with proper rollback on error; static `autofocusFieldForError(string $msg): ?string` centralises WCAG 3.3.1 field-name mapping; `admin/edit.php` reduced 191→162 lines (pure dispatcher + view template); `actions/edit.php` reduced 153→53 lines (CSRF guard + one controller call) - [x] `src/SystemController.php` — extracted all data-fetching logic from `admin/system.php` and `admin/system-fragment.php` into a dedicated controller class; centralises: system status checks (nginx, php-fpm, HTTP ping, SQLite DB, storage dir, maintenance flag) with 2-min TTL caching, PHP environment info (1-hour TTL), disk usage (5-min TTL), log file reading (`readLogTail`), nginx config reading, and the shared CSS-class classifier methods (`logLineClass`, `nginxLineClass`, `statusLabel`, `statusClass`, `humanBytes`, `diskColor`); `system.php` reduced 582→282 lines; `system-fragment.php` reduced 213→137 lines with all `frag_*`-prefixed duplicated helpers removed; both files now purely dispatch to the controller and render view templates - [x] `src/SearchController.php` — extracted all data-fetching logic from `public/search.php` into a dedicated controller class; `SearchController::create()` handles rate-limit enforcement (429 response + exit) and returns a ready instance; `handle()` sanitises GET params, runs all DB queries (`searchTheses`, `countSearchResults`, `getAvailableYears`, `getAllOrientations`, `getAllAPPrograms`, `getUsedTags`, `getPublishedAuthors`), builds the alphabetical author map, assembles OG/meta tags, and returns a flat view-variable array; `public/search.php` reduced from 285 lines to 162 lines (pure dispatcher + view template) - [x] `admin/system.php` + `assets/js/system.js` + `assets/css/system.css` — extracted the large `$extraJsInline` heredoc (≈130 lines) into a static `public/assets/js/system.js` loaded via `$extraJs`; replaced 4 inline `style=` attributes with named CSS modifier classes (`srv-section-title--compact`, `srv-section-title--sub`, `php-grid--flush`, `log-toolbar label` rule); only the dynamic `--disk-pct`/`--disk-color` CSS custom properties remain inline because they carry PHP runtime values - [x] `src/App.php` — removed dead legacy flash key fallback chains from `consumeFlash()`: the `error`, `admin_error`, `edit_error`, `form_error`, `success`, `admin_success`, `edit_success` session keys were never written by any code; all callers already use `App::flash()` → `_flash_error` / `_flash_success`. Method is now 4 lines instead of 18. - [x] `admin/import.php` + `admin.css` — extracted all 4 remaining inline `style=` attributes from `import.php` into named CSS classes (`admin-error-list`, `admin-file-hint`, `admin-import-results`, `admin-import-results__title`) in the Import page section of `admin.css`. No more inline styles in `import.php`. - [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 - [x] WCAG 4.1.2 `