Fix : 'Aller' silencieux sur création de channel + dropdown vide

Deux bugs UX rapportés sur v0.26.0 admin/versions.php :

1. Cliquer 'Aller' avec un nom contenant majuscules / espaces / accents
   ne faisait RIEN. Cause : pattern HTML5 strict (`[a-z0-9_-]{1,64}`) qui
   bloque la submission du form GET sans message visible. Selon le browser
   le tooltip de validation est barely visible, donc le user a l'impression
   que le bouton est cassé.

   Fix : retire le pattern strict, normalise côté serveur :
   « ASTERION VR » → « asterion-vr », « éàç!? » → « » (et on dit pourquoi).
   Flash success qui annonce la normalisation, ou flash error si le nom
   est inexploitable après nettoyage.

2. Après création d'un channel, le dropdown affichait encore '(default)'
   parce que listExistingChannels() filtre sur les versions-*.json
   existants et l'admin n'a pas encore ajouté de version donc le fichier
   n'existe pas. Du coup l'admin pensait que la création n'avait pas
   marché alors que la session était bien sur le nouveau channel.

   Fix : injection forcée du channel actif dans le tableau des options du
   dropdown avec label « X (vide — pas encore de versions) ».

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 08:09:59 +02:00
parent 2b95472393
commit 95c70903e7

View File

@@ -16,15 +16,42 @@ $notesDir = "$root/releasenotes";
// Channel actif pour cette session d'édition. ?channel=X bascule sur
// versions-X.json (créé à la volée si nécessaire), vide = manifest default.
// Whitelist regex anti-injection.
$channel = trim((string)($_GET['channel'] ?? $_POST['__channel'] ?? ''));
if ($channel !== '' && !preg_match('/^[a-z0-9_-]{1,64}$/', $channel)) {
$channel = '';
//
// Normalisation tolérante : on lowercase, on remplace espaces et tirets-cadratins
// par des '-', on retire tout le reste. Comme ça l'admin peut taper "ASTERION VR"
// dans le champ et ça devient "asterion-vr" — pas de submit silencieusement
// bloqué par une regex stricte côté HTML (ce qui faisait rien quand on cliquait
// "Aller" en v0.26.0).
function ps_normalize_channel(string $raw): string
{
$s = strtolower(trim($raw));
// Espaces, tirets typo, tabs, etc. → tiret simple
$s = preg_replace('/[\s\x{2010}-\x{2015}]+/u', '-', $s);
// Retire tout caractère non whitelisté
$s = preg_replace('/[^a-z0-9_-]/', '', $s);
// Compresse les tirets multiples + trim de tirets en début/fin
$s = preg_replace('/-+/', '-', $s);
$s = trim($s, '-_');
return substr($s, 0, 64);
}
$channelRaw = trim((string)($_GET['channel'] ?? $_POST['__channel'] ?? ''));
$channelNormalized = ps_normalize_channel($channelRaw);
$channelWasNormalized = $channelRaw !== '' && $channelNormalized !== $channelRaw;
$channelEmptyAfterNorm = $channelRaw !== '' && $channelNormalized === '';
$channel = $channelNormalized;
$manifestFileName = $channel === '' ? 'versions.json' : "versions-{$channel}.json";
$manifestPath = "$manifestDir/$manifestFileName";
$message = null; $messageType = 'success';
if ($channelEmptyAfterNorm) {
$message = "Nom de channel inexploitable « {$channelRaw} » (que des caractères non autorisés). Tape un nom avec des lettres / chiffres, ex. asterion-vr.";
$messageType = 'error';
} elseif ($channelWasNormalized) {
$message = "Nom de channel normalisé : « {$channelRaw} » → « {$channelNormalized} ».";
$messageType = 'success';
}
/**
* Liste les channels existants (= versions-*.json présents) + ajoute toujours
@@ -268,6 +295,13 @@ foreach ($manifest['versions'] ?? [] as $v) {
}
$existingChannels = listExistingChannels($manifestDir);
// On force la présence du channel actif dans la liste, même si versions-X.json
// n'existe pas encore (cas où l'utilisateur vient de "créer" un channel et n'y
// a pas encore ajouté de version). Sinon le dropdown afficherait "(default)"
// alors que la session est sur le nouveau channel — confusion classique.
if ($channel !== '' && !isset($existingChannels[$channel])) {
$existingChannels[$channel] = $channel . ' (vide — pas encore de versions)';
}
$channelLabel = $channel === '' ? 'default' : $channel;
Layout::header('Versions', 'versions');
@@ -298,8 +332,8 @@ Layout::header('Versions', 'versions');
</span>
<form method="get" style="display: inline-flex; gap: 6px; margin-left: auto;">
<input type="text" name="channel" placeholder="Créer/utiliser un nouveau channel : asterion-vr"
pattern="[a-z0-9_-]{1,64}" required style="width: 280px;"
title="Lettres minuscules, chiffres, _ ou - (max 64 caractères)">
required maxlength="80" style="width: 280px;"
title="Sera normalisé en lowercase + tirets côté serveur (ex. « ASTERION VR » → « asterion-vr »).">
<button class="btn btn-secondary" type="submit">Aller</button>
</form>
</div>