%s', admin_url( 'options-general.php?page=' . self::PAGE_SLUG ), __( 'Settings', 'ai-ready-wp' ) ); array_unshift( $links, $settings_link ); return $links; } public function enqueue_assets( string $hook ): void { if ( 'settings_page_' . self::PAGE_SLUG !== $hook ) { return; } wp_enqueue_style( 'airewp-admin', AIREWP_PLUGIN_URL . 'admin/css/admin.css', [], AIREWP_VERSION ); wp_enqueue_media(); // For logo upload } public function register_settings(): void { register_setting( self::PAGE_SLUG, AIREWP_OPTION_KEY, [ 'type' => 'array', 'sanitize_callback' => [ $this, 'sanitize_settings' ], ] ); // Business Information add_settings_section( self::SECTION_BUSINESS, __( 'Business Information', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Basic information about your business. Used for agents.json, JSON-LD schemas, and AI agent discovery.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( self::SECTION_BUSINESS, 'company_name', __( 'Company Name', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_desc', __( 'Description', 'ai-ready-wp' ), 'textarea' ); $this->add_field( self::SECTION_BUSINESS, 'company_email', __( 'Contact Email', 'ai-ready-wp' ), 'email' ); $this->add_field( self::SECTION_BUSINESS, 'company_phone', __( 'Phone', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_address', __( 'Street Address', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_city', __( 'City', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_zip', __( 'ZIP / Postal Code', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_country', __( 'Country', 'ai-ready-wp' ) ); $this->add_field( self::SECTION_BUSINESS, 'company_logo', __( 'Logo URL', 'ai-ready-wp' ), 'url' ); $this->add_field( self::SECTION_BUSINESS, 'industry', __( 'Industry', 'ai-ready-wp' ), 'select', [ 'options' => [ 'general' => __( 'General Business', 'ai-ready-wp' ), 'restaurant' => __( 'Restaurant / Gastronomy', 'ai-ready-wp' ), 'real-estate' => __( 'Real Estate', 'ai-ready-wp' ), 'ecommerce' => __( 'E-Commerce / Shop', 'ai-ready-wp' ), 'saas' => __( 'SaaS / Software', 'ai-ready-wp' ), 'agency' => __( 'Agency / Consulting', 'ai-ready-wp' ), 'medical' => __( 'Medical / Health', 'ai-ready-wp' ), 'legal' => __( 'Legal / Law Firm', 'ai-ready-wp' ), 'finance' => __( 'Finance / Insurance', 'ai-ready-wp' ), 'education' => __( 'Education / Training', 'ai-ready-wp' ), 'construction' => __( 'Construction / Trades', 'ai-ready-wp' ), 'fitness' => __( 'Fitness / Sports', 'ai-ready-wp' ), 'beauty' => __( 'Beauty / Wellness', 'ai-ready-wp' ), 'automotive' => __( 'Automotive', 'ai-ready-wp' ), 'hotel' => __( 'Hotel / Hospitality', 'ai-ready-wp' ), 'nonprofit' => __( 'Nonprofit / Association', 'ai-ready-wp' ), ], ] ); $this->add_field( self::SECTION_BUSINESS, 'services', __( 'Services (comma-separated)', 'ai-ready-wp' ), 'textarea', [ 'description' => __( 'e.g.: Web Design, SEO, Consulting, Photography', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_BUSINESS, 'schema_type', __( 'Business Type', 'ai-ready-wp' ), 'select', [ 'options' => [ 'Organization' => __( 'Organization (company, brand)', 'ai-ready-wp' ), 'LocalBusiness' => __( 'Local Business (physical location)', 'ai-ready-wp' ), ], ] ); // Social Media add_settings_section( self::SECTION_SOCIAL, __( 'Social Media', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Social profiles for JSON-LD sameAs and agent discovery.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( self::SECTION_SOCIAL, 'social_x', __( 'X (Twitter) URL', 'ai-ready-wp' ), 'url' ); $this->add_field( self::SECTION_SOCIAL, 'social_linkedin', __( 'LinkedIn URL', 'ai-ready-wp' ), 'url' ); $this->add_field( self::SECTION_SOCIAL, 'social_instagram', __( 'Instagram URL', 'ai-ready-wp' ), 'url' ); $this->add_field( self::SECTION_SOCIAL, 'social_facebook', __( 'Facebook URL', 'ai-ready-wp' ), 'url' ); $this->add_field( self::SECTION_SOCIAL, 'social_github', __( 'GitHub URL', 'ai-ready-wp' ), 'url' ); // Feature Toggles add_settings_section( self::SECTION_FEATURES, __( 'Features', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Enable or disable individual AI-Ready features.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( self::SECTION_FEATURES, 'enable_agents_json', __( 'agents.json', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'AI Agent Discovery — /.well-known/agents.json', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_agent_card', __( 'agent-card.json', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'A2A Protocol — /.well-known/agent-card.json', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_llms_txt', __( 'llms.txt', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'LLM-readable content index — /llms.txt', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_json_ld', __( 'JSON-LD Schemas', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Enhanced structured data in page head', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_indexnow', __( 'IndexNow', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Instant indexing notification to Bing/Yandex on content changes', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_rest_api', __( 'REST API', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'API endpoints for AI agents (/wp-json/airewp/v1/)', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_robots_ai', __( 'AI Bot Management', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Manage AI training and retrieval bots in robots.txt', 'ai-ready-wp' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_crawler_analytics', __( 'AI Crawler Analytics', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Track which AI bots visit your site and how often', 'ai-ready-wp' ), ] ); // AI Bot Settings (shown if robots.txt enabled) $settings = AI_Ready_WP::get_settings(); if ( ! empty( $settings['enable_robots_ai'] ) ) { add_settings_section( 'airewp_robots', __( 'AI Bot Policy', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Control which AI bots can access your content. Best practice: allow retrieval bots (search/answers), block training bots.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( 'airewp_robots', 'robots_block_training', __( 'Block Training Bots', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Block bots that scrape for AI model training (GPTBot, ClaudeBot, Google-Extended, Meta-ExternalAgent, etc.)', 'ai-ready-wp' ), ] ); $this->add_field( 'airewp_robots', 'robots_block_retrieval', __( 'Block Retrieval Bots', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Block bots used for AI search answers (OAI-SearchBot, Claude-SearchBot, PerplexityBot). NOT recommended.', 'ai-ready-wp' ), ] ); } // Crawler Analytics Stats (shown if enabled) if ( ! empty( $settings['enable_crawler_analytics'] ) ) { add_settings_section( 'airewp_crawler_stats', __( 'AI Crawler Statistics', 'ai-ready-wp' ), [ $this, 'render_crawler_section' ], self::PAGE_SLUG ); } // ─── v4.0 SEO Module ───────────────────────────────── add_settings_section( 'airewp_seo', __( 'SEO', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Search engine optimization. Meta tags, OpenGraph, XML Sitemap.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( 'airewp_seo', 'enable_seo', __( 'Enable SEO Module', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Meta title/description, OpenGraph tags, Twitter Cards per page', 'ai-ready-wp' ), ] ); // Advanced add_settings_section( self::SECTION_ADVANCED, __( 'Advanced', 'ai-ready-wp' ), fn() => printf( '

%s

', esc_html__( 'Advanced settings for developers and power users.', 'ai-ready-wp' ) ), self::PAGE_SLUG ); $this->add_field( self::SECTION_ADVANCED, 'indexnow_key', __( 'IndexNow Key', 'ai-ready-wp' ), 'text', [ 'description' => __( 'Auto-generated on activation. Only change if you have a custom key.', 'ai-ready-wp' ), 'class' => 'large-text code', ] ); } private function add_field( string $section, string $id, string $label, string $type = 'text', array $args = [] ): void { add_settings_field( $id, $label, [ $this, 'render_field' ], self::PAGE_SLUG, $section, array_merge( $args, [ 'id' => $id, 'type' => $type, ] ) ); } public function render_field( array $args ): void { $settings = AI_Ready_WP::get_settings(); $id = $args['id']; $type = $args['type'] ?? 'text'; $value = $settings[ $id ] ?? ''; $name = AIREWP_OPTION_KEY . '[' . $id . ']'; $class = $args['class'] ?? ''; $desc = $args['description'] ?? ''; switch ( $type ) { case 'textarea': printf( '', esc_attr( $name ), esc_attr( $id ), esc_textarea( $value ) ); break; case 'select': printf( ''; break; case 'checkbox': printf( '', esc_attr( $name ), esc_attr( $id ), checked( $value, true, false ), esc_html( $desc ) ); return; // Don't print description again case 'email': case 'url': case 'text': default: $field_class = $class ?: 'regular-text'; printf( '', esc_attr( $type ), esc_attr( $name ), esc_attr( $id ), esc_attr( $value ), esc_attr( $field_class ) ); break; } if ( $desc && 'checkbox' !== $type ) { printf( '

%s

', esc_html( $desc ) ); } } /** * Render AI Crawler Analytics section. */ public function render_crawler_section(): void { $stats = AIREWP_Crawler_Analytics::get_stats(); $total = AIREWP_Crawler_Analytics::get_total_visits(); $log = AIREWP_Crawler_Analytics::get_log( 10 ); if ( empty( $stats ) ) { printf( '

%s

', esc_html__( 'No AI crawler visits recorded yet. Data appears after bots visit your site.', 'ai-ready-wp' ) ); return; } echo '
'; echo '
'; // Total visits card printf( '
%d%s
', absint( $total ), esc_html__( 'Total AI Visits', 'ai-ready-wp' ) ); // Unique bots card printf( '
%d%s
', count( $stats ), esc_html__( 'Unique Bots', 'ai-ready-wp' ) ); echo '
'; // Per-bot breakdown echo ''; echo ''; echo ''; echo ''; // Sort by visits descending uasort( $stats, fn( $a, $b ) => ( $b['visits'] ?? 0 ) - ( $a['visits'] ?? 0 ) ); foreach ( $stats as $bot => $data ) { printf( '', esc_html( $bot ), absint( $data['visits'] ?? 0 ), esc_html( $data['last_seen'] ?? '—' ) ); } echo '
' . esc_html__( 'Bot', 'ai-ready-wp' ) . '' . esc_html__( 'Visits', 'ai-ready-wp' ) . '' . esc_html__( 'Last Seen', 'ai-ready-wp' ) . '
%s%d%s
'; // Recent log if ( ! empty( $log ) ) { echo '

' . esc_html__( 'Recent Visits', 'ai-ready-wp' ) . '

'; echo ''; echo ''; echo ''; echo ''; echo ''; foreach ( $log as $entry ) { printf( '', esc_html( $entry['bot'] ), esc_html( $entry['url'] ), esc_html( $entry['time'] ) ); } echo '
' . esc_html__( 'Bot', 'ai-ready-wp' ) . '' . esc_html__( 'URL', 'ai-ready-wp' ) . '' . esc_html__( 'Time', 'ai-ready-wp' ) . '
%s%s%s
'; } echo '
'; } public function sanitize_settings( $input ): array { if ( ! is_array( $input ) ) { return []; } $old = AI_Ready_WP::get_settings(); $sanitized = []; // Text fields $text_fields = [ 'company_name', 'company_desc', 'company_phone', 'company_address', 'company_city', 'company_zip', 'company_country', 'services', 'indexnow_key', 'custom_tools', 'custom_skills', ]; foreach ( $text_fields as $field ) { $sanitized[ $field ] = sanitize_text_field( $input[ $field ] ?? $old[ $field ] ?? '' ); } // Email $sanitized['company_email'] = sanitize_email( $input['company_email'] ?? $old['company_email'] ?? '' ); // URLs $url_fields = [ 'company_url', 'company_logo', 'social_x', 'social_linkedin', 'social_instagram', 'social_facebook', 'social_github', ]; foreach ( $url_fields as $field ) { $sanitized[ $field ] = esc_url_raw( $input[ $field ] ?? $old[ $field ] ?? '' ); } // Select fields $sanitized['industry'] = sanitize_text_field( $input['industry'] ?? $old['industry'] ?? 'general' ); $sanitized['schema_type'] = in_array( $input['schema_type'] ?? '', [ 'Organization', 'LocalBusiness' ], true ) ? $input['schema_type'] : 'Organization'; // Checkboxes (unchecked = not in POST) $checkboxes = [ 'enable_agents_json', 'enable_agent_card', 'enable_llms_txt', 'enable_json_ld', 'enable_indexnow', 'enable_rest_api', 'enable_robots_ai', 'enable_crawler_analytics', 'robots_block_training', 'robots_block_retrieval', // v4.0 'enable_seo', ]; foreach ( $checkboxes as $cb ) { $sanitized[ $cb ] = ! empty( $input[ $cb ] ); } // Languages $sanitized['languages'] = $old['languages'] ?? [ get_locale() ]; // Trigger regeneration do_action( 'airewp_settings_updated' ); return $sanitized; } public function render_page(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $settings = AI_Ready_WP::get_settings(); $score = AIREWP_Dashboard::calculate_score( $settings ); ?>

/10

render_file_status( 'agents.json', '.well-known/agents.json', $settings['enable_agents_json'] ); ?> render_file_status( 'agent-card.json', '.well-known/agent-card.json', $settings['enable_agent_card'] ); ?> render_file_status( 'agent.json', '.well-known/agent.json', $settings['enable_agent_card'] ); ?> render_file_status( 'llms.txt', 'llms.txt', $settings['enable_llms_txt'] ); ?> render_file_status( 'llms-full.txt', 'llms-full.txt', $settings['enable_llms_txt'] ); ?> render_file_status( 'IndexNow Key', $settings['indexnow_key'] . '.txt', true ); ?>

render_module_status( __( 'SEO', 'ai-ready-wp' ), $settings['enable_seo'], __( 'Meta tags, OpenGraph, Twitter Cards', 'ai-ready-wp' ) ); ?> render_module_status( __( 'AI Discovery', 'ai-ready-wp' ), $settings['enable_agents_json'], __( 'agents.json, llms.txt, agent-card.json', 'ai-ready-wp' ) ); ?>

.

[ 'class' => [] ] ]; $status = $enabled ? '' . esc_html__( 'Active', 'ai-ready-wp' ) . '' : '' . esc_html__( 'Disabled', 'ai-ready-wp' ) . ''; printf( '%s%s%s', esc_html( $label ), wp_kses( $status, $allowed ), esc_html( $details ) ); } private function render_file_status( string $label, string $path, bool $enabled ): void { global $wp_filesystem; if ( empty( $wp_filesystem ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); } $file_exists = $wp_filesystem->exists( AIREWP_ROOT_PATH . $path ); $url = home_url( '/' . $path ); $allowed_html = [ 'span' => [ 'class' => [] ], 'a' => [ 'href' => [], 'target' => [] ], 'code' => [], ]; if ( ! $enabled ) { $status = '' . esc_html__( 'Disabled', 'ai-ready-wp' ) . ''; } elseif ( $file_exists ) { $status = '✓ ' . esc_html__( 'Generated', 'ai-ready-wp' ) . ''; } else { $status = '✗ ' . esc_html__( 'Missing', 'ai-ready-wp' ) . ''; } $url_col = $enabled && $file_exists ? '' . esc_html( $path ) . '' : '' . esc_html( $path ) . ''; printf( '%s%s%s', esc_html( $label ), wp_kses( $status, $allowed_html ), wp_kses( $url_col, $allowed_html ) ); } }