array( 'label' => __( 'Crawler tracking', 'ai-o-optimiser' ), 'description' => __( 'Logs visits from known AI crawlers and assistants.', 'ai-o-optimiser' ), 'icon' => 'dashicons-search', 'screen' => 'tracking', ), 'enable_referrals' => array( 'label' => __( 'AI referral tracking', 'ai-o-optimiser' ), 'description' => __( 'Logs visits referred by ChatGPT, Perplexity, Claude, Gemini and Copilot.', 'ai-o-optimiser' ), 'icon' => 'dashicons-admin-links', 'screen' => 'tracking', ), 'llms_generate_enabled' => array( 'label' => __( 'AI guidance files', 'ai-o-optimiser' ), 'description' => __( 'Publishes the guidance files AI assistants read instead of crawling every page.', 'ai-o-optimiser' ), 'icon' => 'dashicons-media-document', 'screen' => 'ai-o-optimiser-guidance', ), 'verify_bot_identity' => array( 'label' => __( 'Verify crawler identity', 'ai-o-optimiser' ), 'description' => __( 'Checks that a request claiming to be an AI crawler really came from that company.', 'ai-o-optimiser' ), 'icon' => 'dashicons-shield', 'screen' => 'tracking', ), 'near_miss_capture_enabled' => array( 'label' => __( 'Near-miss capture', 'ai-o-optimiser' ), 'description' => __( 'Stores requests that resemble an AI platform but match no known signature.', 'ai-o-optimiser' ), 'icon' => 'dashicons-filter', 'screen' => 'tracking', ), ); } /** * Settings with every default this row depends on already applied. * * Four of the five keys default through AI_O_Optimiser_Settings; the * guidance switch defaults through the guidance module, which merges into * the same option. Reading the option alone would show a site that has * never saved settings as having everything switched off, which is the * opposite of what it ships as. * * @return array */ public static function settings() { if ( class_exists( 'AI_O_Optimiser_LLMS' ) ) { return AI_O_Optimiser_LLMS::settings(); } return wp_parse_args( get_option( 'ai_o_optimiser_settings', array() ), AI_O_Optimiser_Settings::defaults() ); } /** * The state one card is in. * * "needs_setup" is a modifier on a switch that is on, never a third switch * position: it means the feature is enabled and still cannot do its job. * Only the guidance files can report it, and only from an audit that has * actually run -- a fresh site has never audited anything, and calling that * a setup problem would greet every new install with a warning it did not * earn. * * @param string $key Card key. * @param array $settings Plugin settings. * @return string on|off|needs_setup */ public static function state( $key, array $settings ) { if ( empty( $settings[ $key ] ) ) { return 'off'; } if ( 'llms_generate_enabled' === $key ) { $audit = get_option( 'ai_o_optimiser_llms_audit_cache', array() ); if ( is_array( $audit ) && ! empty( $audit['checked_at'] ) && isset( $audit['overall_status'] ) && 'fail' === $audit['overall_status'] ) { return 'needs_setup'; } } return 'on'; } /** * How many switches are on, out of how many there are. * * A card that needs setup is counted as on, because the switch is on: the * count answers "what have I turned on", and the badge answers "is it * working". Counting it as off would make the line disagree with the * switch sitting next to it. * * @param array $settings Plugin settings. * @return array on and total */ public static function counts( array $settings ) { $on = 0; foreach ( array_keys( self::cards() ) as $key ) { if ( 'off' !== self::state( $key, $settings ) ) { $on++; } } return array( 'on' => $on, 'total' => count( self::cards() ), ); } /** The badge word for a state. */ private static function badge_label( $state ) { if ( 'needs_setup' === $state ) { return __( 'Needs setup', 'ai-o-optimiser' ); } if ( 'on' === $state ) { return __( 'On', 'ai-o-optimiser' ); } return __( 'Off', 'ai-o-optimiser' ); } /** * Where a card's own settings live. * * Four of them are one Settings section; the guidance files have a screen * of their own. Neither link carries a fragment: an anchor that names an id * the page does not have is a link that quietly does nothing. * * @param array $card One card definition. * @return string */ private static function settings_url( array $card ) { if ( 'tracking' === $card['screen'] ) { return admin_url( 'admin.php?page=ai-o-optimiser-settings§ion=tracking' ); } return admin_url( 'admin.php?page=' . rawurlencode( $card['screen'] ) ); } /** Where a switch returns to. */ private static function return_url() { return admin_url( 'admin.php?page=ai-o-optimiser-dashboard' ) . '#' . self::ANCHOR; } /** * Print the row. * * The Dash is readable by editors through the dashboard capability filter, * but changing what a site records is an administrator's decision: a reader * without the sensitive capability gets the same cards with a link to * Settings where the switch would be, rather than a control that would * refuse them at the other end. * * @return void */ public static function render() { $settings = self::settings(); $counts = self::counts( $settings ); $can_edit = current_user_can( AI_O_Optimiser_SENSITIVE_CAPABILITY ); ?>

$card ) : ?>