%s', esc_url( admin_url( 'options-general.php?page=' . self::PAGE_SLUG ) ), esc_html__( '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' ), ] ); $this->add_field( self::SECTION_FEATURES, 'enable_mcp_json', __( 'mcp.json', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'MCP Server Discovery — /.well-known/mcp.json', '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' ), [ $this, 'render_robots_section_intro' ], self::PAGE_SLUG ); $this->add_field( 'airewp_robots', 'robots_block_training', __( 'Block Training Bots', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Answer GPTBot, ClaudeBot, Google-Extended, Meta-ExternalAgent and the other training crawlers with Disallow. Your content then stays out of future models, and out of the answers those models give.', 'ai-ready-wp' ), ] ); $this->add_field( 'airewp_robots', 'robots_block_retrieval', __( 'Block Retrieval Bots', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Answer OAI-SearchBot, Claude-SearchBot, PerplexityBot and the other retrieval crawlers with Disallow. Your site then no longer appears in AI answers. 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' ), [ $this, 'render_seo_section_intro' ], 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' ), ] ); // Only offered when there is something to override. if ( '' !== AIREWP_SEO::competing_plugin() ) { $this->add_field( 'airewp_seo', 'seo_force_with_plugin', __( 'Output anyway', 'ai-ready-wp' ), 'checkbox', [ 'description' => __( 'Print this module\'s meta tags, social tags and sitemap even though another SEO plugin is active. Expect duplicate tags.', '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', ] ); $this->add_field( self::SECTION_ADVANCED, 'mcp_servers', __( 'MCP Servers (JSON)', 'ai-ready-wp' ), 'textarea', [ 'description' => __( 'JSON array of MCP servers. Example: [{"name":"my-server","description":"My MCP Server","transport":"streamable-http","url":"https://example.com/mcp"}]', 'ai-ready-wp' ), ] ); // Connect your AI (v4.5): the five steps to the hosted MCP server. add_settings_section( self::SECTION_CONNECT, __( 'Connect your AI (MCP)', 'ai-ready-wp' ), [ $this, 'render_connect_section' ], self::PAGE_SLUG ); } 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 '| ' . esc_html__( 'Bot', 'ai-ready-wp' ) . ' | '; echo '' . esc_html__( 'Visits', 'ai-ready-wp' ) . ' | '; echo '' . esc_html__( 'Last Seen', 'ai-ready-wp' ) . ' |
|---|---|---|
| %s | %d | %s |
| ' . esc_html__( 'Bot', 'ai-ready-wp' ) . ' | '; echo '' . esc_html__( 'URL', 'ai-ready-wp' ) . ' | '; echo '' . esc_html__( 'Time', 'ai-ready-wp' ) . ' | '; echo '
|---|---|---|
| %s | %s | %s |
https://wp.studiomeyer.io/mcp',
'claude mcp add --transport http ai-ready-wp https://wp.studiomeyer.io/mcp'
),
__( 'Ask your AI to connect this site. It will hand you a secure link where you paste the site address, your WordPress username and the application password. Nothing is typed into the chat.', 'ai-ready-wp' ),
__( 'From then on your AI reads the readiness score, the pages and robots.txt. With AI-Ready WP Pro it may also write: settings, titles and descriptions, rebuilds.', 'ai-ready-wp' ),
];
echo '%s
', esc_html__( 'Credentials are encrypted on the StudioMeyer server and are only used to talk to this site\'s REST API.', 'ai-ready-wp' ) ); // WordPress only offers application passwords over HTTPS. On an // http:// site the profile section from step 2 does not exist, and // a step that points at a section that is not there is the kind of // instruction that costs an afternoon. if ( ! wp_is_application_passwords_available() ) { printf( '%s
', esc_html__( 'Application passwords need HTTPS. This site is served over HTTP, so WordPress hides that section until the site runs on HTTPS.', 'ai-ready-wp' ) ); } } /** * Intro of the SEO section: names the other SEO plugin when there is one. */ public function render_seo_section_intro(): void { $other = AIREWP_SEO::competing_plugin(); if ( '' === $other ) { printf( '%s
', esc_html__( 'Search engine optimization. Meta tags, OpenGraph, XML Sitemap.', 'ai-ready-wp' ) ); return; } printf( '%s
', esc_html( sprintf( /* translators: %s: name of the other SEO plugin */ __( '%s is active. AI-Ready WP leaves titles, descriptions, social tags, the sitemap and the organisation schema to it, so your pages do not carry two of each. agents.json, llms.txt, robots.txt and the FAQ schema are not affected.', 'ai-ready-wp' ), $other ) ) ); } /** * The SEO row of the module table, with the standing-down state. */ private function render_seo_module_status( array $settings ): void { $other = AIREWP_SEO::competing_plugin(); if ( ! empty( $settings['enable_seo'] ) && '' !== $other && AIREWP_SEO::yields() ) { printf( '%s'; echo '' . esc_html__( 'AI-Ready WP now lets AI crawlers in.', 'ai-ready-wp' ) . ' '; echo esc_html__( 'Your robots.txt used to turn away GPTBot, ClaudeBot, Google-Extended and ten other AI crawlers, a default this plugin set without asking. Since that stands in the way of the AI visibility the plugin is for, the block is now off. If you do want to keep your content out of model training, switch it back on under AI Bot Policy.', 'ai-ready-wp' ); echo ' ' . esc_html__( 'Open the settings', 'ai-ready-wp' ) . ''; echo ' · ' . esc_html__( 'Understood', 'ai-ready-wp' ) . ''; echo '
' . esc_html( $path ) . ''
: '' . esc_html( $path ) . '';
printf(
'
' . esc_html( $target ) . ''
);
?>
' . esc_html( $info['owner'] ) . '',
'' . esc_html( $info['php_user'] ) . ''
);
?>
chown -R