docs: frontend & template audit — add sections D–H to TODO.md

Analysed all public pages, CSS files, and template partials. Found:

Template structure (D):
- <head> boilerplate duplicated across 5 public pages (no shared partial exists)
- Live-reload snippet copy-pasted into 6 files
- templates/header.php and templates/head.php are dead/orphaned files
- public/assets/icons.svg is a dead TrumboWYG sprite (never referenced, ~15 KB)
- admin_favicon.svg used as public favicon (misleading naming)

CSS (E):
- html/body reset block repeated in 4 page stylesheets; belongs in common.css
- @font-face missing font-display:swap (FOIT risk)
- Search pagination is fully inline-styled; home page already has .pagination-btn classes
- Multiple one-off inline styles across tfe.php, edit.php, index.php
- .site-nav__right is a CSS duplicate of .site-nav__link
- .site-nav__link--active applied in PHP but has no CSS rule (invisible active state)

Template logic (F):
- 429 rate-limit response is bare unstyled HTML
- apropos.php contacts/credits hardcoded (require code deploy to change)
- licence.php wastes half the viewport with an always-empty right column

Accessibility (G):
- <nav> has no aria-label; search <form> has no accessible name
- No <meta name=description> on any public page
- No Open Graph tags anywhere (blank previews when sharing thesis links)

Minor (H):
- thanks.php duplicates getThesisFiles() with a raw query
- admin/index.php stats broken when filters are active (PHP array_filter on subset)
This commit is contained in:
Pontoporeia
2026-03-26 22:51:16 +01:00
parent 72daf46c46
commit 7d836c165c

115
TODO.md
View File

@@ -447,3 +447,118 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
- [ ] **`__wakeup()` singleton guard throws from a public method** — PHP 8.x deprecates
throwing exceptions from `__wakeup`. Change to `trigger_error(…, E_USER_ERROR)` or implement
`__serialize()`/`__unserialize()` that always throw.
---
## Refactor & Maintenance — Templates & Frontend (audit 2026-03-26)
### D — Template structure / boilerplate duplication
- [ ] **Every public page duplicates its own `<head>`**`index.php`, `search.php`, `tfe.php`,
`apropos.php`, `licence.php` each contain an identical block: `<!DOCTYPE html>`,
`<html lang="fr">`, `<meta charset>`, `<meta viewport>`, `<link rel="icon">`,
`<link modern-normalize>`, `<link common.css>`, live-reload script. Only `<title>` and one
extra CSS `<link>` differ. Extract a `templates/public/head.php` partial accepting
`$pageTitle` and `$extraCss` — mirrors the pattern `templates/admin/head.php` already uses.
- [ ] **Live-reload snippet copy-pasted into 6 files**`index.php`, `search.php`, `tfe.php`,
`apropos.php`, `licence.php`, `templates/admin/head.php` all contain the same 6-line
`(function poll(){…})()` block. Consolidate into the shared head partials.
- [ ] **`templates/header.php` and `templates/head.php` are dead files** — neither is `include`d
anywhere in the codebase. Both contain outdated markup from a previous design iteration
(`lang="en"`, empty author meta, a broken nav with double-quoted `href` attributes inside
`href`). Delete both to remove confusion.
- [ ] **`public/assets/icons.svg` is dead** — it is the full TrumboWYG editor icon sprite (40+
symbols) referenced **nowhere** in the codebase. The only WYSIWYG editor in use (EasyMDE
in `pages-edit.php`) loads from CDN. Delete `icons.svg` (~15 KB of noise).
- [ ] **`admin_favicon.svg` used as the public-facing favicon** — every public page links
`/assets/admin_favicon.svg`. Rename or create a distinct `favicon.svg` so admin and public
can diverge without naming confusion.
### E — CSS architecture
- [ ] **`html, body { margin:0; padding:0; height:100% }` repeated in 4 page stylesheets** —
`main.css`, `search.css`, `tfe.css`, `apropos.css` all open with this identical block.
Move it to `common.css` once; delete from the four files. Same for the body-level flex-column
shell (`display:flex; flex-direction:column; background:var(--white)`) which only differs in
the BEM class name applied to `<body>`.
- [ ] **No `font-display` on the `Combinedd.otf` custom font**`common.css` declares `@font-face`
with no `font-display` property; the browser blocks text rendering until the font loads (FOIT).
Add `font-display: swap`. Also add a `<link rel="preload">` for the font file in the shared
head partial once it exists.
- [ ] **Search results pagination is fully inline-styled**`search.php` lines 159164 apply
`style="padding:.25rem .7rem;border:1px solid #ddd;…"` and hardcoded `#ddd`/`#666`. The home
page (`index.php`) already has `.pagination-btn` / `.pagination-info` in `main.css`. Reuse
those classes in `search.php` and remove the inline styles.
- [ ] **Scattered inline styles in templates** — notable instances that should become named classes:
- `tfe.php` line 146: `style="align-items:start;"``.tfe-meta-item--top` in `tfe.css`
- `tfe.php` lines 148, 170172, 193: `font-style:italic`, `margin-top:1.5rem`,
`font-size:.88rem;color:#666`, `color:#999;font-style:italic``.tfe-note-value`,
`.tfe-back-link`, `.tfe-restricted` in `tfe.css`
- `admin/edit.php`: multiple `style=` on `.admin-form-row` and banner preview → modifier
classes in `admin.css`
- `index.php` line 146: `style="padding:2rem;color:#666;"``.cards-empty` in `main.css`
- [ ] **`.site-nav__right` is a duplicate of `.site-nav__link`** — `common.css` defines both with
identical declarations (font-size, letter-spacing, text-transform, color, opacity, transition).
The only difference is DOM position. Merge `.site-nav__right` into `.site-nav__link`; let the
flex layout position it via `margin-left:auto` or DOM order.
- [ ] **`.site-nav__link--active` is applied in `nav.php` but never defined in CSS** — the class
is set conditionally but has no corresponding rule in `common.css`, so the active state is
invisible. Add a visible style (e.g. `opacity:1; border-bottom:1px solid rgba(255,255,255,.6)`)
or remove the conditional.
### F — Template logic / PHP in templates
- [ ] **Rate-limit 429 response in `search.php` emits unstyled bare HTML** — the early-exit block
outputs `<!DOCTYPE html><html><body><h1>Trop de requêtes</h1>…` with no stylesheet, no lang,
no viewport meta. Style it inline-minimally or redirect to a consistent `429.php` page (like
`maintenance.php`).
- [ ] **`apropos.php` contacts and credits are hardcoded in the template** — names, roles, emails
(Laurent Leprince, Xavier Gorgol, Brigitte Ledune) and credits text live in PHP/HTML and
require a code deploy to change. Either move them into the `about` page Markdown (admin-
editable) or extract to a config array so they are in one place.
- [ ] **`licence.php` wastes half the viewport with an always-empty right column** — the page
reuses the two-column `.apropos-layout` but `<div class="apropos-right"></div>` is always
empty. Add a `.apropos-layout--single` variant (or just `grid-template-columns:1fr` when
the right child is empty) to use the full width for content.
### G — Accessibility & semantics
- [ ] **`<nav>` in `nav.php` has no `aria-label`** — pages have multiple landmark regions (main
nav, search `<form>`, pagination). Add `aria-label="Navigation principale"` to the `<nav>`
and `aria-label="Pagination"` to pagination wrappers so screen readers can distinguish them.
- [ ] **Search bar `<form>` has no accessible name**`search-bar.php` has no `aria-label` on the
`<form>` and no `<label>` for the input (only a placeholder). Add `aria-label="Recherche"` to
the `<form>` element.
- [ ] **No `<meta name="description">` on any public page** — all public pages omit the description
meta tag (the dead `templates/head.php` had `content=""`). Add per-page descriptions:
site blurb for `index.php`, synopsis excerpt for `tfe.php`, page content intro for
`apropos.php`/`licence.php`. Necessary for search indexing and link preview cards.
- [ ] **No Open Graph tags**`tfe.php` is the ideal candidate for `og:title`, `og:description`
(synopsis), `og:image` (banner or cover path through `/media.php`), `og:type=article`.
Without them, sharing a thesis link on social media or messaging apps shows a blank preview.
### H — Minor / low-hanging fruit
- [ ] **`admin/thanks.php` duplicates `getThesisFiles()` with a raw PDO query** — lines 3440
manually prepare `SELECT … FROM thesis_files WHERE thesis_id = ?` instead of calling
`$db->getThesisFiles($thesisId)` which already exists. Replace with the DB method.
- [ ] **`admin/index.php` stats computed via PHP `array_filter` on full result set** — "total",
"publiés", "en attente" counts are derived by filtering the already-fetched `$theses` array
in PHP. When a filter is active the stats reflect only filtered rows, which is misleading.
Add `Database::getThesesStats(): array` returning three counts from SQL
(`COUNT(*)`, `SUM(is_published)`, `SUM(1-is_published)`) so they always reflect the full DB.