self::SLUG, 'feature' => $feature]), admin_url('admin.php')); } /** * The features in the order they are shown. * * @return array */ public static function features(): array { return [ 'assistant' => [ 'title' => __('Site assistant', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('A chat that answers questions from your posts and links to them.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('chat_enabled')), 'url' => AssistantPage::pageUrl(), 'index' => true, 'tab' => '', 'screen' => null, 'section' => '', ], 'related' => [ 'title' => __('Related posts', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Lists the posts closest in meaning, under each post. Uses no neurons.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('related_enabled')), 'url' => self::url('related'), 'index' => true, 'tab' => 'related', 'screen' => [Screens\RelatedScreen::class, 'render'], 'section' => 'aipcf_related', ], 'search' => [ 'title' => __('Search', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Finds posts by meaning, not only by the words typed.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('search_enabled')), 'url' => self::url('search'), 'index' => true, 'tab' => 'search', 'screen' => [Screens\SearchScreen::class, 'render'], 'section' => 'aipcf_search', ], 'links' => [ 'title' => __('Link assistant', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Suggests internal links for a post, with the exact words to link.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('links_enabled')), 'url' => self::url('links'), 'index' => true, 'tab' => 'links', 'screen' => [Screens\LinksScreen::class, 'render'], 'section' => 'aipcf_links', ], 'insights' => [ 'title' => __('Search insights', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Records what people search for and shows what your posts do not answer yet.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('insights_enabled')), 'url' => self::url('insights'), 'index' => true, 'tab' => 'insights', 'screen' => [Screens\InsightsScreen::class, 'render'], 'section' => 'aipcf_insights', ], 'agents' => [ 'title' => __('AI agents', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Lets AI agents such as Claude and ChatGPT search and quote your site.', 'aipcf-ai-provider-for-cloudflare'), 'on' => !empty(Settings::get('agents_enabled')), 'url' => self::url('agents'), 'index' => true, 'tab' => 'agents', 'screen' => [Screens\AgentsScreen::class, 'render'], 'section' => '', 'after' => [Screens\AgentsScreen::class, 'tryIt'], ], 'backfill' => [ 'title' => __('Backfill', 'aipcf-ai-provider-for-cloudflare'), 'text' => __('Writes missing meta descriptions and image alt text in the background.', 'aipcf-ai-provider-for-cloudflare'), 'on' => Backfill::enabled(), 'url' => self::url('backfill'), 'index' => false, 'tab' => 'backfill', 'screen' => [Screens\BackfillScreen::class, 'render'], 'section' => '', ], ]; } public static function render(): void { if (!current_user_can('manage_options')) { return; } $features = self::features(); $current = isset($_GET['feature']) ? sanitize_key(wp_unslash((string) $_GET['feature'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if (isset($features[$current]) && $features[$current]['tab'] !== '') { self::renderFeature($features[$current]); return; } echo '
'; echo Ui::header(__('Features', 'aipcf-ai-provider-for-cloudflare'), __('What AIPCF does for your site. Each one is off until you switch it on.', 'aipcf-ai-provider-for-cloudflare'), SettingsPage::connectionPillUi()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes echo '
'; echo '
'; if (!EmbeddingSettings::enabled()) { echo Ui::notice(__('Most features search your content by meaning. Build the content index first, on Settings.', 'aipcf-ai-provider-for-cloudflare'), 'info'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes } $cards = ''; foreach ($features as $f) { $needsIndex = $f['index'] && !EmbeddingSettings::enabled(); $actions = [['label' => $f['tab'] === '' ? __('Open', 'aipcf-ai-provider-for-cloudflare') : __('Settings', 'aipcf-ai-provider-for-cloudflare'), 'url' => $f['url']]]; if ($needsIndex) { $actions[] = ['label' => __('Build the content index', 'aipcf-ai-provider-for-cloudflare'), 'url' => SettingsPage::settingsUrl('content-index'), 'kind' => 'link']; } $cards .= Ui::card($f['title'], Ui::actions($actions), [ 'text' => $f['text'], 'pill' => $f['on'] ? ($needsIndex ? ['warn', __('Needs the content index', 'aipcf-ai-provider-for-cloudflare')] : ['ok', __('On', 'aipcf-ai-provider-for-cloudflare')]) : ['', __('Off', 'aipcf-ai-provider-for-cloudflare')], ]); } echo Ui::grid($cards); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes echo '
'; } /** * One feature's page: its settings, saved as its own tab. * * @param array{title: string, text: string, on: bool, url: string, index: bool, tab: string, screen: callable|null, section: string, after?: callable} $f */ private static function renderFeature(array $f): void { echo '
'; echo Ui::header($f['title'], $f['text'], $f['on'] ? ['ok', __('On', 'aipcf-ai-provider-for-cloudflare')] : ['', __('Off', 'aipcf-ai-provider-for-cloudflare')]); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes echo '
'; echo Ui::back(__('Features', 'aipcf-ai-provider-for-cloudflare'), self::url()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes $html = $f['screen'] !== null ? (string) call_user_func($f['screen']) : SettingsPage::legacySections(SettingsPage::tabPage('content'), [$f['section']]); SettingsPage::form($f['tab'], $html); if (isset($f['after'])) { echo '
' . (string) call_user_func($f['after']) . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ui escapes } echo '
'; } }