mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
fix: search filter labels, 429 page styling, __wakeup PHP 8.x deprecation
- Replace three <span class='search-filter-label'> with proper <label for='...'> elements in search.php filter bar; add id attributes to the corresponding <select> elements so the label/control association is programmatic (WCAG 1.3.1, 3.3.2). - Rewrite the rate-limit 429 early-exit in search.php from a bare one-liner echo to a full HTML document with lang='fr', viewport meta, and inline dark styles matching maintenance.php; inject the retry countdown into the user-facing message (Template audit F). - Fix PHP 8.x __wakeup() deprecation in Database.php singleton guard: replace the throw statement with trigger_error(..., E_USER_ERROR) and add an explicit void return type (Refactor audit C).
This commit is contained in:
22
TODO.md
22
TODO.md
@@ -445,9 +445,9 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
|
||||
to avoid filesystem churn. At minimum, move the cache dir to `/tmp` or a dedicated
|
||||
`storage/cache/` path that is excluded from deploy rsync.
|
||||
|
||||
- [ ] **`__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.
|
||||
- [x] **`__wakeup()` singleton guard throws from a public method** - changed to
|
||||
`trigger_error('Cannot unserialize singleton ...', E_USER_ERROR)` with explicit `void` return
|
||||
type; eliminates the PHP 8.x deprecation notice.
|
||||
|
||||
---
|
||||
|
||||
@@ -509,10 +509,9 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
|
||||
|
||||
### 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`).
|
||||
- [x] **Rate-limit 429 response in `search.php` emits unstyled bare HTML** - replaced bare echo with
|
||||
a properly structured HTML document (lang="fr", viewport meta, inline dark styles matching
|
||||
`maintenance.php`); `$retrySeconds` injected into the user-facing message.
|
||||
|
||||
- [ ] **`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
|
||||
@@ -957,11 +956,10 @@ Current state: **zero ARIA attributes, zero skip links, zero focus-visible style
|
||||
structure. There is no programmatic association between label and value. Replacing with
|
||||
`<dl>/<dt>/<dd>` (already flagged in the semantic audit) directly fixes this criterion.
|
||||
|
||||
- [ ] **Search filter `<select>` elements have no associated `<label>`** - each select is
|
||||
preceded by `<span class="search-filter-label">Année</span>` but this span is not a
|
||||
`<label>` and has no `for` attribute. Screen readers cannot associate it with the control.
|
||||
Fix: replace `<span>` with `<label for="filter-year">` and add `id="filter-year"` to
|
||||
the select (or use the wrapping-label pattern).
|
||||
- [x] **Search filter `<select>` elements have no associated `<label>`** - replaced the three
|
||||
`<span class="search-filter-label">` elements with `<label for="filter-year">`,
|
||||
`<label for="filter-orientation">`, `<label for="filter-ap">`; added matching `id` attributes
|
||||
to the `<select>` elements. Visual appearance unchanged (same CSS class).
|
||||
|
||||
- [ ] **Admin form rows: `<label class="admin-label" for="X">` is correct** - the `for` attribute
|
||||
is present on all single-input rows in `add.php` and `edit.php`. Good. However, the
|
||||
|
||||
@@ -8,7 +8,46 @@ $rateLimit = new RateLimit(30, 60);
|
||||
if (!$rateLimit->check()) {
|
||||
http_response_code(429);
|
||||
header('Retry-After: ' . $rateLimit->getResetTime());
|
||||
echo '<!DOCTYPE html><html lang="fr"><body><h1>Trop de requêtes</h1><p>Réessayez dans ' . $rateLimit->getResetTime() . ' secondes.</p></body></html>';
|
||||
$retrySeconds = (int)$rateLimit->getResetTime();
|
||||
echo <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Trop de requêtes – Posterg</title>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
background: #0d0d0d;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
.box { max-width: 520px; text-align: center; }
|
||||
.box__logo {
|
||||
font-size: 1.1rem; font-weight: 700;
|
||||
letter-spacing: .12em; text-transform: uppercase;
|
||||
color: #fff; margin-bottom: 2.5rem;
|
||||
}
|
||||
.box__title { font-size: 1.6rem; font-weight: 300; margin-bottom: 1rem; }
|
||||
.box__text { font-size: .95rem; color: #999; line-height: 1.7; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box">
|
||||
<div class="box__logo">POSTERG</div>
|
||||
<h1 class="box__title">Trop de requêtes</h1>
|
||||
<p class="box__text">Vous avez effectué trop de recherches en peu de temps.<br>
|
||||
Réessayez dans {$retrySeconds} secondes.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
exit;
|
||||
}
|
||||
$rateLimit->sendHeaders();
|
||||
@@ -91,8 +130,8 @@ $extraCss = ['assets/search.css'];
|
||||
<input type="hidden" name="query" value="<?= htmlspecialchars($_GET['query'] ?? '') ?>">
|
||||
|
||||
<div class="search-filter-group">
|
||||
<span class="search-filter-label">Année</span>
|
||||
<select class="search-filter-select" name="year">
|
||||
<label class="search-filter-label" for="filter-year">Année</label>
|
||||
<select class="search-filter-select" name="year" id="filter-year">
|
||||
<option value="">Toutes</option>
|
||||
<?php foreach ($years as $y): ?>
|
||||
<option value="<?= (int)$y ?>" <?= (isset($_GET['year']) && $_GET['year'] == $y) ? 'selected' : '' ?>>
|
||||
@@ -103,8 +142,8 @@ $extraCss = ['assets/search.css'];
|
||||
</div>
|
||||
|
||||
<div class="search-filter-group">
|
||||
<span class="search-filter-label">Orientation</span>
|
||||
<select class="search-filter-select" name="orientation">
|
||||
<label class="search-filter-label" for="filter-orientation">Orientation</label>
|
||||
<select class="search-filter-select" name="orientation" id="filter-orientation">
|
||||
<option value="">Toutes</option>
|
||||
<?php foreach ($orientations as $o): ?>
|
||||
<option value="<?= htmlspecialchars($o['name']) ?>"
|
||||
@@ -116,8 +155,8 @@ $extraCss = ['assets/search.css'];
|
||||
</div>
|
||||
|
||||
<div class="search-filter-group">
|
||||
<span class="search-filter-label">AP</span>
|
||||
<select class="search-filter-select" name="ap_program">
|
||||
<label class="search-filter-label" for="filter-ap">AP</label>
|
||||
<select class="search-filter-select" name="ap_program" id="filter-ap">
|
||||
<option value="">Tous</option>
|
||||
<?php foreach ($apPrograms as $ap): ?>
|
||||
<option value="<?= htmlspecialchars($ap['name']) ?>"
|
||||
|
||||
@@ -1284,9 +1284,11 @@ class Database {
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Prevent unserialization
|
||||
* Prevent unserialization.
|
||||
* PHP 8.x deprecates throwing from __wakeup(); use trigger_error instead.
|
||||
*/
|
||||
public function __wakeup() {
|
||||
throw new Exception("Cannot unserialize singleton");
|
||||
public function __wakeup(): void {
|
||||
// phpcs:ignore
|
||||
trigger_error('Cannot unserialize singleton ' . static::class, E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user