'Méthode non autorisée.']); exit; } if ( !isset($_POST['csrf_token'], $_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']) ) { http_response_code(403); header('Content-Type: application/json'); echo json_encode(['error' => 'Token de sécurité invalide.']); exit; } // ── Determine draft key ───────────────────────────────────────────────── $draftToken = $_POST['draft_token'] ?? ''; $thesisId = (int)($_POST['thesis_id'] ?? 0); if ($draftToken !== '' && preg_match('/^[a-f0-9]{16}$/', $draftToken)) { $draftKey = 'admin_draft_' . $draftToken; } elseif ($thesisId > 0) { $draftKey = 'admin_draft_edit_' . $thesisId; } else { http_response_code(400); header('Content-Type: application/json'); echo json_encode(['error' => 'Paramètres invalides.']); exit; } // ── Save all form fields ──────────────────────────────────────────────── $excludePrefixes = [ 'csrf_token', 'share_link_token', 'filepond_mode', 'queue_file', 'filepond_', ]; $excludeExact = ['draft_token', 'thesis_id', 'slug', 'couverture', 'note_intention', 'files', 'annexes', 'peertube_video', 'peertube_audio', 'cover_remove', 'go', 'MAX_FILE_SIZE']; $draft = []; foreach ($_POST as $key => $value) { if (in_array($key, $excludeExact, true)) continue; $skip = false; foreach ($excludePrefixes as $prefix) { if (str_starts_with($key, $prefix)) { $skip = true; break; } } if ($skip) continue; if ($value === '' || $value === null || (is_array($value) && count($value) === 0)) { continue; } $draft[$key] = $value; } $_SESSION[$draftKey] = $draft; // NOTE: Do NOT rotate the CSRF token here. // Rotating it breaks concurrent requests: // 1. FilePond uploads in flight use the old token (from ) // and fail when the server session already has the new token. // 2. Overlapping autosave requests hit CSRF mismatch. // 3. HTMX fragment requests (pill-search, language-autre) can't use the old token. // The CSRF token already rotates on page load and form submit — that's sufficient. // Autosave is a background persistence mechanism and does not need token rotation. header('Content-Type: application/json'); echo json_encode([ 'success' => true, ]); exit;