' . ' ' . htmlspecialchars($message) . '' . ''; exit; } function hxToastError(string $message): never { $id = 'toast-' . bin2hex(random_bytes(4)); http_response_code(200); echo '' . ''; exit; } if ($section === 'formulaire_restrictions') { // HTMX may not send unchecked checkboxes even with hidden 0-value inputs; // missing key means unchecked → treat as '0'. $rawPost = $_POST['restricted_files_enabled'] ?? '(missing)'; $newValue = empty($_POST['restricted_files_enabled']) ? '0' : '1'; error_log('[settings.php] SAVE formulaire_restrictions | restricted_files_enabled raw=' . var_export($rawPost, true) . ' | resolved=' . $newValue); $db->setSetting('restricted_files_enabled', $newValue); $logger->logFormSettingsUpdate(['restricted_files_enabled' => $newValue]); if ($isHxRequest) { hxToastSuccess('Restrictions d\'accès aux fichiers mises à jour.'); } else { App::flash('success', "Paramètres mis à jour."); } } elseif ($section === 'formulaire_acces') { $allowed = [ 'access_type_libre_enabled', 'access_type_interne_enabled', 'access_type_interdit_enabled', ]; $newValues = []; foreach ($allowed as $key) { $raw = $_POST[$key] ?? '(missing)'; $value = empty($_POST[$key]) ? '0' : '1'; error_log('[settings.php] SAVE formulaire_acces | ' . $key . ' raw=' . var_export($raw, true) . ' | resolved=' . $value); $db->setSetting($key, $value); $newValues[$key] = $value; } $logger->logFormSettingsUpdate($newValues); if ($isHxRequest) { hxToastSuccess("Degrés d'ouverture mis à jour."); } else { App::flash('success', "Degrés d'ouverture mis à jour."); } } elseif ($section === 'objet_types') { $rawThese = $_POST['objet_these_enabled'] ?? '(missing)'; $rawFrart = $_POST['objet_frart_enabled'] ?? '(missing)'; $newValues = [ 'objet_these_enabled' => empty($_POST['objet_these_enabled']) ? '0' : '1', 'objet_frart_enabled' => empty($_POST['objet_frart_enabled']) ? '0' : '1', ]; error_log('[settings.php] SAVE objet_types | objet_these_enabled raw=' . var_export($rawThese, true) . ' | resolved=' . $newValues['objet_these_enabled']); error_log('[settings.php] SAVE objet_types | objet_frart_enabled raw=' . var_export($rawFrart, true) . ' | resolved=' . $newValues['objet_frart_enabled']); $db->setSetting('objet_these_enabled', $newValues['objet_these_enabled']); $db->setSetting('objet_frart_enabled', $newValues['objet_frart_enabled']); $logger->logObjetTypesUpdate($newValues); if ($isHxRequest) { hxToastSuccess('Types de travaux mis à jour.'); } else { App::flash('success', "Types de travaux mis à jour."); } } elseif ($section === 'smtp') { $smtpData = [ 'host' => $_POST['smtp_host'] ?? '', 'port' => $_POST['smtp_port'] ?? 587, 'encryption' => $_POST['smtp_encryption'] ?? 'tls', 'username' => $_POST['smtp_username'] ?? '', 'from_email' => $_POST['smtp_from_email'] ?? '', 'from_name' => $_POST['smtp_from_name'] ?? 'XAMXAM', 'notify_email' => $_POST['smtp_notify_email'] ?? '', ]; // Only update password when user actually typed something. $pwd = $_POST['smtp_password'] ?? ''; if ($pwd !== '') { $smtpData['password'] = $pwd; } SmtpRelay::updateSettings($db, $smtpData); // Immediately probe the server to validate credentials $test = SmtpRelay::test($db); $logger->logSmtpUpdate($test['ok']); if ($test['ok']) { App::flash('success', "Paramètres SMTP mis à jour — connexion validée ✓"); } else { App::flash('error', "Paramètres sauvegardés, mais le test de connexion a échoué : " . $test['error']); if ($test['field'] !== null) { $_SESSION['_flash_smtp_field'] = $test['field']; } } } elseif ($section === 'peertube') { // Feature flag $enabled = isset($_POST['peertube_upload_enabled']) ? '1' : '0'; $db->setSetting('peertube_upload_enabled', $enabled); // Credentials — only overwrite password when user typed something $data = [ 'instance_url' => $_POST['peertube_instance_url'] ?? '', 'username' => $_POST['peertube_username'] ?? '', 'channel_id' => $_POST['peertube_channel_id'] ?? 1, 'privacy' => $_POST['peertube_privacy'] ?? 1, ]; $pwd = $_POST['peertube_password'] ?? ''; if ($pwd !== '') { $data['password'] = $pwd; } PeerTubeService::updateSettings($db, $data); $logger->logPeerTubeUpdate($enabled === '1'); App::flash('success', 'Paramètres PeerTube mis à jour.'); } else { App::flash('error', "Section inconnue."); } // Centralised HTMX response — each section above already called hxToast* and exited. // If we get here as an HTMX request from an unhandled section, return empty 200. if ($isHxRequest) { http_response_code(200); exit; } $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Redirect back to wherever the form came from, defaulting to parametres $redirect = '/admin/parametres.php'; if (in_array($section, ['formulaire_restrictions', 'formulaire_acces', 'objet_types'], true)) { $redirect = '/admin/contenus.php'; } header('Location: ' . $redirect); exit;