false]; $id = $_GET['rj_agent_preview'] ?? ''; if (!is_string($id) || !raijuAgentValidChangesetId($id) || !raijuAccessIsLoggedIn()) return false; $cs = raijuAgentChangesetLoad($id); $user = raijuAgentUserKey(); if ($cs === null || $cs['status'] !== 'ready' || $user === '' || !hash_equals((string)$cs['owner'], $user)) return false; if (raijuAgentConflicts($cs)) return false; $replay = raijuAgentReplayOps($cs['ops']); if (!$replay['ok']) return false; $news = []; $events = []; foreach (raijuAgentChangedFiles($replay['state']) as $file => $change) { if (($key = raijuAgentParseTargetFromFile($file)) !== null) { raijuContentOverlaySet($key, (array)$change['after']); } elseif (raijuAgentTargetKindOfFile($file) === 'news') { $news[basename($file, '.json')] = (array)$change['after']; } elseif (raijuAgentTargetKindOfFile($file) === 'events') { $before = []; foreach ((array)($change['before']['events'] ?? []) as $event) $before[(string)($event['id'] ?? '')] = $event; foreach ((array)($change['after']['events'] ?? []) as $event) { $eventId = (string)($event['id'] ?? ''); if (($before[$eventId] ?? null) !== $event) $events[$eventId] = $event; } } } $GLOBALS['raijuNewsOverlay'] = $news; $GLOBALS['raijuEventsOverlay'] = $events; $GLOBALS['raijuContentReadonlyRuntime'] = true; $GLOBALS['raijuAgentPreview'] = ['active' => true, 'id' => $id]; // Staged images are not in the library yet: the page loads them from the review endpoint instead. $staged = []; foreach ((array)$cs['media'] as $ref => $media) { $staged[(string)$media['finalPath']] = '/admin/api.php?action=ai-agent-media&id=' . $id . '&ref=' . $ref; // inside HTML attributes } if ($staged) ob_start(static fn(string $html): string => strtr($html, $staged)); if (!headers_sent()) { header('X-Robots-Tag: noindex, nofollow'); header('Cache-Control: no-store, max-age=0'); } return true; } /** Text for the preview bar in the admin language (the page may be in another language). */ function raijuAgentPreviewText(string $key, string $fallback): string { if (!function_exists('t')) { $i18n = dirname(__DIR__, 2) . '/admin/lang/i18n.php'; if (is_file($i18n)) require_once $i18n; } if (!function_exists('t')) return $fallback; $text = t($key); return $text === $key ? $fallback : $text; } /** * The page the person came from (the assistant sends it as rj_agent_return): a path on this site, nothing else. * Empty when it is missing or not a plain local path. */ function raijuAgentPreviewReturnPath(): string { $path = $_GET['rj_agent_return'] ?? ''; if (!is_string($path) || $path === '' || strlen($path) > 500 || $path[0] !== '/' || str_starts_with($path, '//')) return ''; if (preg_match('/[\x00-\x1f\x7f\\\\]/', $path) || str_contains($path, '://')) return ''; return preg_replace('/([?&])rj_agent_[a-z_]+=[^]*(&|$)/', '$1', $path) ?? ''; } /** * Bar above the page while previewing: what this is, and the way on. Both buttons are plain links back to the * page the person came from; the assistant there (same tab, it still has the package) opens, and for "Apply * changes" asks for its own confirmation. The preview itself never applies anything. */ function raijuAgentPreviewBanner(): string { $id = (string)($GLOBALS['raijuAgentPreview']['id'] ?? ''); $return = rtrim(raijuAgentPreviewReturnPath(), '?&'); if ($return === '') $return = (string)($GLOBALS['basePath'] ?? '') !== '' ? (string)$GLOBALS['basePath'] : './'; $banner = raijuAgentPreviewText('agent.preview_banner', 'Preview: these changes have not been applied yet. Only you see the page like this.'); $apply = raijuAgentPreviewText('agent.apply', 'Apply changes'); $back = raijuAgentPreviewText('agent.preview_back', 'Back to the assistant'); $link = 'display:inline-block;font:600 13px/1 system-ui,sans-serif;padding:.5rem .85rem;border-radius:6px;border:1px solid #fff;text-decoration:none;margin:0 .25rem'; $href = static fn(string $hash): string => htmlspecialchars($return . $hash, ENT_QUOTES); return '
'; }