Files
xamxam/TODO.md
Pontoporeia d51cd62088 Extract last inline style from header.php into admin.css
The SVG icon in the admin nav's public-site link had two inline styles:
  style="vertical-align:middle;margin-right:0.4em"

Moved to a new CSS rule:
  .admin-body header nav > a svg { vertical-align: middle; margin-right: 0.4em; }

templates/header.php now contains zero style= attributes.
The only remaining inline styles project-wide are:
  - dynamic gradient (hsl computed from $item['id']) in public/index.php — legitimately dynamic
  - --disk-pct/--disk-color custom properties in system.php — carry PHP runtime values
2026-04-06 15:33:08 +02:00

15 KiB
Raw Blame History

TODO

Pending tasks have been split into topic files under todo/:

File Topic
todo/01-css-semantic-refactor.md CSS class audit, semantic HTML (public + admin), inline style extraction, favicon
todo/02-php-components.md Form field partials, shared UI partials, controller extraction, backend maintenance
todo/03-system-cache.md system_cache table, SystemCache class, system.php refactor
todo/04-accessibility.md WCAG 2.1 AA - remaining failures grouped by success criterion

Recently completed (this session)

  • templates/header.php + admin.css — extracted last remaining inline style= from any shared template: style="vertical-align:middle;margin-right:0.4em" on the admin-nav public-site SVG icon moved to .admin-body header nav > a svg rule in admin.css; templates/header.php now contains zero style= attributes; only the dynamic gradient (hsl(<?= $hue ?>)) in public/index.php and the PHP-runtime --disk-pct/--disk-color custom properties in system.php remain (both legitimately dynamic)

  • admin/thanks.php + admin/pages.php + admin.css — extracted last 3 inline style= attributes from admin PHP templates: <div style="margin-top:1.5rem;display:flex;gap:.75rem;flex-wrap:wrap;"> in thanks.phpclass="admin-action-bar" (.admin-action-bar rule added to CSS); <p style="color:var(--text-secondary);"> in thanks.phpclass="admin-muted" (.admin-muted rule added); style="font-size:.8rem;padding:.3rem .75rem;" on Éditer button in pages.phpclass="admin-btn admin-btn--sm" (uses existing modifier). Only the dynamic --disk-pct/--disk-color CSS custom properties on the disk bar in system.php remain (carry PHP runtime values — legitimate).

  • variables.css + search.css — public dark-mode support via @media (prefers-color-scheme: dark) scoped to body:not(.admin-body): all semantic tokens (--bg-*, --text-*, --border-*, --accent-*, status colours) overridden with dark equivalents; --accent-primary lightened to #b87fd4 for WCAG contrast on dark backgrounds; --search-error-* variables added to replace hardcoded #fff0f0/#c00 in .search-error; admin pages unaffected

  • src/ThesisCreateController.php — extracted all validation, DB writes, and file-upload handling from admin/actions/formulaire.php into a dedicated controller; make() factory instantiates Database; loadFormData() returns all lookup tables for the add-form view; submit(post, files) runs the full creation flow: validates/sanitises all POST fields, calls findOrCreateAuthor, inserts thesis row via createThesis, links jury/languages/formats/tags inside a transaction, then processes cover image, banner, and multi-file uploads outside the transaction; autofocusFieldForError() maps exception messages to WCAG 3.3.1 autofocus field hints; actions/formulaire.php reduced 346→45 lines (CSRF guard + one submit() call); admin/add.php lookup-table block replaced with ThesisCreateController::make()->loadFormData(); Database::setPublished() and Database::bulkSetPublished() added, eliminating raw SQL from actions/publish.php (100→65 lines); no raw PDO calls remain in any action handler file

  • src/HomeController.php — extracted all data-fetching logic from public/index.php into a dedicated controller class; create() returns a ready instance with Database singleton injected; handle() parses page/year GET params, determines display mode (default-random-latest / year-filtered / paginated-all), runs the appropriate DB queries (getLatestPublishedYear, getLatestYearTheses, searchTheses+countSearchResults, getPublishedTheses+countPublishedTheses), batch-loads cover images via getCoverPathsForTheses, assembles OG/meta tags, and returns a flat view-variable array; public/index.php reduced 100→71 lines (6-line dispatcher + pure view template); todo/02-php-components.md “Extract remaining controllers” task marked done

  • src/TfeController.php — extracted all data-fetching, OG-tag assembly, and view-variable construction from public/tfe.php into a dedicated controller class; create() returns a ready instance with Database singleton injected; handle() validates the id param (redirects on missing/invalid), loads the thesis row via getThesisById(), calls getThesisAccessTypeId() for visibility gating, builds the meta description (strip_tags + 160-char truncation), resolves the OG image (banner_path → first image file → empty), assembles the full $ogTags array (type/title/description/url/image/image_alt/site_name/article_author/article_published_time), collects WebVTT caption paths for N-th-video pairing, and returns a flat view-variable array; captionFiles replaces inline $_captionFiles array in the view; $db reference removed from tfe.php entirely; tfe.php reduced 271→206 lines (9-line dispatcher + pure view template); todo/02-php-components.md “Extract remaining controllers” and “Move OG tag construction into controller logic” tasks updated

  • 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)

  • 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

  • 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)

  • 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

  • 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.

  • 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.

  • 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.)

  • 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

  • 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

  • WCAG 4.1.2 <video> captions — tfe.php now emits <track kind="captions"> for each MP4 when a .vtt sidecar exists (N-th VTT paired with N-th video). formulaire.php accepts .vtt uploads (file_type='caption', MIME normalised). media.php serves text/vtt with correct headers and visibility gating. Admin add.php file-field hint documents the .vtt upload convention.

  • admin/edit.php — WCAG 4.1.2: removed mb_strimwidth truncation from $accessOptions mapping; access type <select> options now include full description text (name — description) so the accessible name is unambiguous for screen readers

  • public/assets/favicon.svg — created public favicon: brand-purple (#9557b5) rounded square with white "P" lettermark; distinct from admin_favicon.svg (archive-restore icon in #c104fc)

  • templates/head.php — favicon <link> now selects favicon.svg (public) vs admin_favicon.svg (admin) based on $isAdmin; closes todo/01-css-semantic-refactor.md favicon task

  • todo/04-accessibility.md — marked WCAG 3.1.1 lang audit and WCAG 4.1.2 select truncation items as done

  • todo/01-css-semantic-refactor.md — marked favicon task as done

  • admin.css - added [aria-current="page"] rule for admin nav links (border-bottom: 2px solid currentColor; padding-bottom: 1px) fixing WCAG 1.4.1 (active nav link had no non-colour indicator)

  • admin.css - fixed undefined --admin-purple variable in pagination hover; replaced with --accent-primary (same #9557b5 value)

  • todo/01-css-semantic-refactor.md - audited all CSS/HTML refactor tasks; marked ~15 items as already-done (.admin-main, .admin-page-title, .admin-form-row, .admin-label, .admin-input/select/textarea, .admin-table, .admin-fieldset, tfe.css class replacements, search.css selector, login.php/edit.php inline styles, admin-alert replacement, form partial hints)

  • todo/04-accessibility.md - marked WCAG 1.4.1 admin nav and --admin-purple audit items as completed

  • admin/index.php - server-side pagination (25/page); Database::getThesesListCount() added; getThesesList() extended with $limit/$offset; access_type JOIN added to query (was missing); result-count meta line added; .pagination-wrap + .pagination-btn + .pagination-info styles added to admin.css

  • checkbox-list.php - replaced <div class="admin-checkbox-list"> with <fieldset class="admin-checkbox-group"><legend class="sr-only">...</legend><ul> (WCAG 1.3.1 fix)

  • admin.css - replaced .admin-checkbox-list with .admin-body fieldset.admin-checkbox-group > ul semantic selectors; added span.admin-row-label as visible label column counterpart

  • login.php - wrapped content in <main id="main-content"> landmark

  • account.php - <div class="admin-account-status"><dl>; __row divs kept; __label spans → <dt>; admin-danger-zone__description div → <p>

  • index.php - maintenance bar <div><aside role="status" aria-label="Statut du site">

  • add.php / edit.php - autocomplete="name" on author field, autocomplete="email" on contact field

  • tags.php - all inline style= attributes removed; sizing/spacing moved to CSS (.admin-input--inline, .admin-select--inline, .admin-inline-form + .admin-inline-form, .admin-tags-count)

  • Marked already-done items in todo files: stats <dl>, thanks.php <section>, scope="col" on both tables, tfe.php inline styles, role="alert" on flash messages

  • admin-submit-wrapadmin-form-footer rename: updated all 6 admin templates (add.php, edit.php, login.php, account.php, import.php, pages-edit.php) and all 8 CSS selectors in admin.css (.admin-form > div:not(...) exclusion guards, .admin-login-box overrides). Closes todo/01-css-semantic-refactor.md submit-wrap task.

  • Marked status-badge.php partial and WCAG 1.3.1 status-badge items as already-done in todo/02-php-components.md and todo/04-accessibility.md (partial + CSS were fully implemented; TODO had not been updated)

  • public/index.php — WCAG 2.4.4: home page cards now append <span class="sr-only">, YEAR</span> to each cards <p> link text so screen readers get unique link names when two theses share the same title

  • todo/04-accessibility.md — WCAG 2.4.3: marked back-link focus-order item as already done (tfe-back-link is already the first DOM element in .tfe-left, before <h1>)

  • jury-fieldset.php — WCAG 3.3.2: replaced bare <label>Lecteur·ices :</label> with <fieldset class="admin-jury-lecteurs"><legend>Lecteur·ices</legend> so the lecteur group has a proper programmatic label

  • jury-fieldset.php — WCAG 4.1.2: all "Externe" checkboxes (promoteur + each lecteur row, static + dynamically added via JS) now carry aria-label="[Role] — externe" providing group context without visible redundancy

  • jury-fieldset.php — WCAG 2.1.1: jury remove buttons verified to have descriptive aria-label="Supprimer le lecteur·ice N" on all rows (static + dynamic)

  • admin.css — added .admin-body fieldset fieldset.admin-jury-lecteurs rule: strips border/background on the nested lecteur fieldset so it renders as a visual sub-group, not a double-bordered card

  • todo/04-accessibility.md — WCAG 1.4.4 + 1.4.12 audited and marked done: all font-sizes are rem; no overflow:hidden on essential text content