mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-07 11:39:18 +02:00
script-src 'self' 'unsafe-inline' added to admin Content-Security-Policy. default-src 'self' was blocking OverType editor init block and the dev live-reload poller. Admin section is auth-gated so unsafe-inline is acceptable.
95 lines
17 KiB
Markdown
95 lines
17 KiB
Markdown
# 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] `scripts/deploy-server.sh` — add step 4: `systemctl reload nginx` runs automatically; remove manual reload instruction from justfile
|
||
|
||
- [x] `nginx/posterg.conf` — add `script-src 'self' 'unsafe-inline'` to admin CSP; `default-src 'self'` was blocking OverType init script and live-reload poller
|
||
|
||
- [x] `RateLimit.php` — silence `mkdir()` with `@`; guard `file_put_contents` with `is_writable()` check so permission errors degrade gracefully (allow request, skip write) instead of spamming nginx error log
|
||
- [x] `scripts/deploy-server.sh` + `scripts/setup-server.sh` — add `mkdir -p storage/cache/rate_limit` + `chown`/`chmod 2775` so php-fpm has write access on fresh deploys
|
||
|
||
- [x] CSS cleanup — removed dark-mode `@media (prefers-color-scheme: dark)` block from `variables.css`; deleted dead `colors.css` reference file; added all missing semantic tokens to `variables.css` (`--header-*`, `--search-error-*`, `--sys-*` dark-terminal tokens, `--*-muted-bg/border/hover` alpha overlay tokens); replaced every hardcoded hex/rgba in `common.css`, `main.css`, `search.css`, `admin.css`, `system.css` with `var()` references; fixed `--border-color` typo → `--border-primary` in `search.css`; fixed `view-toggle__btn` active color to use `--accent-foreground`; updated `README.md`; admin and public now share identical token set with zero duplication
|
||
|
||
- [x] `pages-edit.php` — replaced EasyMDE (333 KB: 320 KB JS + 13 KB CSS) with OverType (118 KB, single JS file, no CSS); removed `easymde.min.js` and `easymde.min.css` from `public/assets/`; vendored `overtype.min.js`; replaced `<textarea name="content">` + 60-line EasyMDE toolbar/SVG init block with `<input type="hidden" name="content">` + `<div id="editor">` mount target + 6-line OverType init; `onChange` callback keeps the hidden input in sync for form submission; net saving: ~215 KB, ~54 lines of JS
|
||
|
||
- [x] `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)
|
||
|
||
- [x] `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.php` → `class="admin-action-bar"` (`.admin-action-bar` rule added to CSS); `<p style="color:var(--text-secondary);">` in `thanks.php` → `class="admin-muted"` (`.admin-muted` rule added); `style="font-size:.8rem;padding:.3rem .75rem;"` on Éditer button in `pages.php` → `class="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).
|
||
|
||
- [x] `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
|
||
|
||
|
||
- [x] `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
|
||
- [x] `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
|
||
|
||
- [x] `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
|
||
|
||
- [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` — fixed PHP parse error: straight apostrophe in `'Responsable des mémoires de l'ERG :'` broke string literal; replaced with curly apostrophe (U+2019); also removed stray blank line introduced by earlier edit
|
||
- [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 `<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.
|
||
|
||
|
||
- [x] `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
|
||
- [x] `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`)
|
||
- [x] `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
|
||
- [x] `todo/04-accessibility.md` — marked WCAG 3.1.1 lang audit and WCAG 4.1.2 select truncation items as done
|
||
- [x] `todo/01-css-semantic-refactor.md` — marked favicon task as done
|
||
|
||
|
||
- [x] `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)
|
||
- [x] `admin.css` - fixed undefined `--admin-purple` variable in pagination hover; replaced with `--accent-primary` (same `#9557b5` value)
|
||
- [x] `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)
|
||
- [x] `todo/04-accessibility.md` - marked WCAG 1.4.1 admin nav and `--admin-purple` audit items as completed
|
||
|
||
- [x] `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`
|
||
|
||
- [x] `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)
|
||
- [x] `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
|
||
- [x] `login.php` - wrapped content in `<main id="main-content">` landmark
|
||
- [x] `account.php` - `<div class="admin-account-status">` → `<dl>`; `__row` divs kept; `__label` spans → `<dt>`; `admin-danger-zone__description` div → `<p>`
|
||
- [x] `index.php` - maintenance bar `<div>` → `<aside role="status" aria-label="Statut du site">`
|
||
- [x] `add.php` / `edit.php` - `autocomplete="name"` on author field, `autocomplete="email"` on contact field
|
||
- [x] `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`)
|
||
- [x] 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
|
||
|
||
- [x] `admin-submit-wrap` → `admin-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.
|
||
- [x] 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)
|
||
|
||
- [x] `public/index.php` — WCAG 2.4.4: home page cards now append `<span class="sr-only">, YEAR</span>` to each card’s `<p>` link text so screen readers get unique link names when two theses share the same title
|
||
- [x] `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>`)
|
||
|
||
- [x] `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
|
||
- [x] `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
|
||
- [x] `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)
|
||
- [x] `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
|
||
- [x] `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
|