mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
fix: duplicate warning not shown in admin, double-encoded in partage, no focus
- toast-fragment.php: 204 early-exit now also checks flash['warning']; previously the warning was consumed by consumeFlash() then silently dropped - partage/index.php: store warning as plain text; htmlspecialchars() applied once at render time — previously htmlspecialchars() was called inside the stored string then again at output, producing ' entities etc. - partage/index.php: flash-warning div gets id + tabindex=-1; inline JS scrolls it into view and focuses it on DOMContentLoaded - admin/footer.php: htmx:afterSettle listener focuses .toast--warning after HTMX injects the toast fragment into #toast-region
This commit is contained in:
@@ -13,7 +13,7 @@ AdminAuth::requireLogin();
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
error_log(sprintf(
|
||||
'CSRF token validation failed in formulaire.php — POST token: %s, SESSION token: %s',
|
||||
'CSRF token validation failed in formulaire.php - POST token: %s, SESSION token: %s',
|
||||
$_POST['csrf_token'] ?? '(missing)',
|
||||
$_SESSION['csrf_token'] ?? '(missing)'
|
||||
));
|
||||
@@ -49,10 +49,10 @@ try {
|
||||
|
||||
// Build a warning with a clickable link to the existing thesis.
|
||||
$existingUrl = htmlspecialchars('/admin/edit.php?id=' . $e->existingThesisId);
|
||||
$existingRef = htmlspecialchars($e->existingIdentifier . ' — ' . $e->existingTitle . ' (' . $e->existingYear . ')');
|
||||
$warningHtml = 'Doublon détecté : un TFE très similaire existe déjà. '
|
||||
. '<a href="' . $existingUrl . '" style="color:inherit;text-decoration:underline">' . $existingRef . '</a>'
|
||||
. ' Vérifiez avant de soumettre à nouveau.';
|
||||
$existingRef = htmlspecialchars($e->existingIdentifier . ' - ' . $e->existingTitle . ' (' . $e->existingYear . ')');
|
||||
$warningHtml = 'Doublon détecté : un TFE très similaire existe déjà.'
|
||||
. '<br><a href="' . $existingUrl . '">' . $existingRef . '</a>'
|
||||
. '<br>Vérifiez avant de soumettre à nouveau.';
|
||||
|
||||
App::flash('warning', $warningHtml);
|
||||
$_SESSION['form_data'] = $_POST;
|
||||
|
||||
@@ -13,7 +13,7 @@ AdminAuth::requireLogin();
|
||||
|
||||
$flash = App::consumeFlash();
|
||||
|
||||
if (!$flash['error'] && !$flash['success']) {
|
||||
if (!$flash['error'] && !$flash['success'] && !$flash['warning']) {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--warning);
|
||||
color: var(--text-primary);
|
||||
animation: toast-enter 0.35s ease-out; /* no fade-out — stays until dismissed */
|
||||
}
|
||||
|
||||
.toast--warning a {
|
||||
|
||||
@@ -360,6 +360,7 @@ label:has(+ div > input:required)::after {
|
||||
background: var(--warning-muted-bg, rgba(251,202,81,.12));
|
||||
border-color: var(--warning-muted-border, rgba(251,202,81,.35));
|
||||
color: var(--text-primary);
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
/* ── Share link badge ───────────────────────────────────────────────────── */
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Partage — Entry point for shared student submission forms.
|
||||
* Partage - Entry point for shared student submission forms.
|
||||
*
|
||||
* Routes:
|
||||
* /partage/<slug> — Render the share-link form (or password gate)
|
||||
* /partage/<slug>/submit — POST endpoint for form submissions via share link
|
||||
* /partage/recapitulatif.php?id=N — Post-submission confirmation page
|
||||
* /partage/<slug> - Render the share-link form (or password gate)
|
||||
* /partage/<slug>/submit - POST endpoint for form submissions via share link
|
||||
* /partage/recapitulatif.php?id=N - Post-submission confirmation page
|
||||
*/
|
||||
require_once __DIR__ . '/../../bootstrap.php';
|
||||
|
||||
@@ -83,7 +83,7 @@ if (!$validationResult['valid']) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Link is valid — render the form
|
||||
// Link is valid - render the form
|
||||
$link = $validationResult['link'];
|
||||
renderShareLinkForm($slug, $link);
|
||||
|
||||
@@ -222,7 +222,7 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
|
||||
// Build old()-compatible callable from $formData (share forms use the array variant).
|
||||
$shareOldFn = fn(string $key, string $default = '') => old($formData, $key, $default);
|
||||
// No autofocus in the share form — identity function.
|
||||
// No autofocus in the share form - identity function.
|
||||
$shareWithAutofocusFn = fn(string $field, array $attrs = []) => $attrs;
|
||||
|
||||
// Load all form help blocks in one query.
|
||||
@@ -267,7 +267,8 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
<div class="flash-error" role="alert"><?= htmlspecialchars($flashError) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($flashWarning): ?>
|
||||
<div class="flash-warning" role="alert"><?= htmlspecialchars($flashWarning) ?></div>
|
||||
<div class="flash-warning" id="flash-warning" role="alert" tabindex="-1"><?= htmlspecialchars($flashWarning) ?></div>
|
||||
<script>document.addEventListener('DOMContentLoaded',function(){var el=document.getElementById('flash-warning');if(el){el.scrollIntoView({behavior:'smooth',block:'center'});el.focus();}});</script>
|
||||
<?php endif; ?>
|
||||
<?php if ($flashSuccess): ?>
|
||||
<div class="flash-success" role="alert"><?= htmlspecialchars($flashSuccess) ?></div>
|
||||
@@ -460,7 +461,7 @@ function handleShareLinkSubmission(string $slug): void
|
||||
unset($_SESSION[$shareCsrfKey]);
|
||||
unset($_SESSION['share_verified_' . $slug]);
|
||||
|
||||
// Send confirmation e-mail — on delivery failure, redirect to retry page
|
||||
// Send confirmation e-mail - on delivery failure, redirect to retry page
|
||||
$emailError = null;
|
||||
try {
|
||||
$emailSent = StudentEmail::sendConfirmation(Database::getInstance(), $thesisId, $_POST);
|
||||
@@ -472,7 +473,7 @@ function handleShareLinkSubmission(string $slug): void
|
||||
header('Location: /partage/retry-email?id=' . urlencode((string)$thesisId));
|
||||
exit();
|
||||
}
|
||||
// Non-recipient errors (relay down, etc.) — skip email silently
|
||||
// Non-recipient errors (relay down, etc.) - skip email silently
|
||||
$_SESSION['share_email_sent'] = false;
|
||||
}
|
||||
|
||||
@@ -488,9 +489,10 @@ function handleShareLinkSubmission(string $slug): void
|
||||
error_log('Share link duplicate submission: ' . $e->getMessage());
|
||||
|
||||
// Repopulate the form and surface a clear warning to the student.
|
||||
$_SESSION['_flash_warning'] = 'Votre soumission ressemble à un TFE déjà enregistré ('
|
||||
. htmlspecialchars($e->existingIdentifier . ' — ' . $e->existingTitle . ', ' . $e->existingYear)
|
||||
. '). Si vous pensez qu’il s’agit d’une erreur, veuillez contacter l’équipe.';
|
||||
// Store as plain text — htmlspecialchars() is applied at render time.
|
||||
$_SESSION['_flash_warning'] = 'Votre soumission ressemble à un TFE déjà enregistré.'
|
||||
. "\n" . $e->existingIdentifier . ' — ' . $e->existingTitle . ' (' . $e->existingYear . ')'
|
||||
. "\nSi vous pensez qu'il s'agit d'une erreur, veuillez contacter l'équipe.";
|
||||
$_SESSION['form_data_share_' . $slug] = $_POST;
|
||||
$_SESSION[$shareCsrfKey] = bin2hex(random_bytes(32)); // Regenerate token
|
||||
|
||||
|
||||
Reference in New Issue
Block a user