$postId, 'lang' => $lang, 'title' => $title, 'slug' => $slug, 'date' => $date, 'author' => trim($post['author'] ?? ''), 'excerpt' => trim($post['excerpt'] ?? ''), 'image' => trim($post['image'] ?? ''), 'content' => sanitizeHtml((string)($post['content'] ?? '')), 'hidden' => !empty($post['hidden']), 'lastModified' => date('c'), ]; $filepath = $newsDir . $postId . '.json'; $result = raijuJsonAtomicWrite($filepath, $sanitized); if ($result === false) { jsonResponse(false, null, 'Error saving post'); } if ($oldId !== '' && $oldId !== $postId) { @unlink($newsDir . $oldId . '.json'); } jsonResponse(true, $sanitized, 'Post saved'); break; case 'toggle-news-status': if (!validateCsrfToken()) { jsonResponse(false, null, 'Invalid CSRF token'); } $postId = $_POST['post_id'] ?? ''; if (empty($postId) || !preg_match('/^[a-z0-9][a-z0-9-]*$/', $postId)) { jsonResponse(false, null, 'Invalid post ID'); } $newsDir = dirname(CONTENT_PATH) . '/news/'; $filepath = $newsDir . $postId . '.json'; if (!is_file($filepath)) { jsonResponse(false, null, 'Post not found'); } $post = json_decode(file_get_contents($filepath), true); if (!is_array($post)) { jsonResponse(false, null, 'Invalid post data'); } $post['hidden'] = !($post['hidden'] ?? false); $post['lastModified'] = date('c'); $result = file_put_contents( $filepath, json_encode($post, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), LOCK_EX ); if ($result === false) { jsonResponse(false, null, 'Error updating post'); } jsonResponse(true, ['hidden' => $post['hidden']]); break; case 'delete-news': if (!validateCsrfToken()) { jsonResponse(false, null, 'Invalid CSRF token'); } $postId = $_POST['post_id'] ?? ''; if (empty($postId) || !preg_match('/^[a-z0-9][a-z0-9-]*$/', $postId)) { jsonResponse(false, null, 'Invalid post ID'); } $newsDir = dirname(CONTENT_PATH) . '/news/'; $filepath = $newsDir . $postId . '.json'; if (!is_file($filepath)) { jsonResponse(false, null, 'Post not found'); } if (!unlink($filepath)) { jsonResponse(false, null, 'Error deleting post'); } jsonResponse(true, null, 'Post deleted'); break; // ============================================================ // MAIL MANAGEMENT // ============================================================ }