*/ public function defaults() { $public_post_types = $this->get_public_post_type_names(); $default_posttypes = array_values( array_intersect( array( 'page', 'post', 'product' ), $public_post_types ) ); if ( empty( $default_posttypes ) ) { $default_posttypes = array_values( array_slice( $public_post_types, 0, 2 ) ); } return array( 'enabled' => 1, 'full_enabled' => 1, 'generation_mode' => 'auto', 'site_type' => 'business', 'site_purpose' => '', 'custom_intro' => '', 'manual_llms' => '', 'include_post_types' => $default_posttypes, 'include_taxonomies' => array_values( array_intersect( array( 'category', 'post_tag', 'product_cat' ), $this->get_public_taxonomy_names() ) ), 'respect_noindex' => 1, 'include_private' => 0, 'include_attachments' => 0, 'max_items' => 80, 'full_max_items' => 500, 'regeneration' => 'manual', 'last_generated' => '', 'add_robots_reference' => 1, 'safe_mode' => 1, 'wizard_completed' => 0, 'last_scan_score' => 0, 'last_scan_status' => '', 'last_scan_time' => '', 'report_branding' => 1, 'webmcp_enabled' => 1, 'webmcp_form_metadata_enabled' => 1, 'webmcp_search_form_enabled' => 1, 'webmcp_content_forms_enabled' => 1, 'webmcp_confirmation_required' => 1, 'webmcp_tool_prefix' => 'site', 'webmcp_default_form_description' => __( 'Submits a public website form. Agents should request user confirmation before submitting.', 'agizoai-agentic-browsing-optimizer' ), 'agent_tools_enabled' => 1, 'agent_tools_rest_enabled' => 1, 'agent_tools_search_enabled' => 1, 'agent_tools_content_enabled' => 1, 'agent_tools_recent_enabled' => 1, 'agent_tools_menus_enabled' => 1, 'agent_tools_taxonomies_enabled' => 1, 'abilities_enabled' => 1, 'abilities_public_enabled' => 1, 'ai_profile_enabled' => 1, 'site_health_enabled' => 1, 'accessibility_scanner_enabled' => 1, 'accessibility_scan_limit' => 25, 'layout_scanner_enabled' => 1, 'layout_scan_limit' => 25, 'layout_helper_css_enabled' => 0, 'layout_reserve_ad_slots' => 0, 'layout_top_ad_min_height' => 90, 'layout_inline_ad_min_height' => 250, 'layout_sidebar_ad_min_height' => 280, 'layout_mobile_ad_min_height' => 250, 'layout_sticky_header_offset' => 0, 'ecommerce_scanner_enabled' => 1, 'ecommerce_scan_limit' => 25, 'ecommerce_product_tools_enabled' => 1, 'ecommerce_product_search_enabled' => 1, 'ecommerce_product_details_enabled' => 1, 'ecommerce_product_categories_enabled' => 1, 'ecommerce_include_out_of_stock' => 1, 'integrations_enabled' => 1, 'integration_real_seo_master_enabled' => 1, 'integration_yoast_enabled' => 1, 'integration_rankmath_enabled' => 1, 'integration_aioseo_enabled' => 1, 'integration_seopress_enabled' => 1, 'integration_wordpress_core_enabled' => 1, 'integration_primary_source' => 'auto', 'integration_use_seo_descriptions_in_llms' => 1, 'integration_expose_seo_metadata_in_tools' => 1, 'integration_add_seo_notes_to_llms' => 1, 'integration_scan_limit' => 25, ); } /** * Ensure options exist. * * @return void */ public function ensure_defaults() { $current = get_option( AGIZOAI_AGENTIC_OPTION ); if ( ! is_array( $current ) ) { add_option( AGIZOAI_AGENTIC_OPTION, $this->defaults(), '', false ); return; } $merged = wp_parse_args( $current, $this->defaults() ); update_option( AGIZOAI_AGENTIC_OPTION, $this->sanitize_options( $merged ), false ); } /** * Register settings. * * @return void */ public function register() { register_setting( 'agizoai_agentic_settings', AGIZOAI_AGENTIC_OPTION, array( 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_options' ), 'default' => $this->defaults(), ) ); } /** * Get all options merged with defaults. * * @return array */ public function get_options() { $options = get_option( AGIZOAI_AGENTIC_OPTION, array() ); if ( ! is_array( $options ) ) { $options = array(); } return wp_parse_args( $options, $this->defaults() ); } /** * Get one option value. * * @param string $key Option key. * @param mixed $default Default value. * @return mixed */ public function get( $key, $default = null ) { $options = $this->get_options(); return array_key_exists( $key, $options ) ? $options[ $key ] : $default; } /** * Update a single option. * * @param string $key Option key. * @param mixed $value Option value. * @return void */ public function update( $key, $value ) { $options = $this->get_options(); $options[ $key ] = $value; update_option( AGIZOAI_AGENTIC_OPTION, $this->sanitize_options( $options ), false ); } /** * Return supported site type labels. * * @return array */ public function get_site_types() { return array( 'business' => __( 'Business / Local Service', 'agizoai-agentic-browsing-optimizer' ), 'blog' => __( 'Blog / Publisher', 'agizoai-agentic-browsing-optimizer' ), 'news' => __( 'News / Magazine', 'agizoai-agentic-browsing-optimizer' ), 'ecommerce' => __( 'WooCommerce / Shop', 'agizoai-agentic-browsing-optimizer' ), 'school' => __( 'School / Education', 'agizoai-agentic-browsing-optimizer' ), 'directory' => __( 'Directory / Listings', 'agizoai-agentic-browsing-optimizer' ), 'sports' => __( 'Sports / Fixtures / Tips', 'agizoai-agentic-browsing-optimizer' ), 'custom' => __( 'Custom Website', 'agizoai-agentic-browsing-optimizer' ), ); } /** * Sanitize settings array. * * @param mixed $input Raw settings. * @return array */ public function sanitize_options( $input ) { $defaults = $this->defaults(); $input = is_array( $input ) ? $input : array(); $output = $defaults; $output['enabled'] = empty( $input['enabled'] ) ? 0 : 1; $output['full_enabled'] = empty( $input['full_enabled'] ) ? 0 : 1; $output['respect_noindex'] = empty( $input['respect_noindex'] ) ? 0 : 1; $output['include_private'] = empty( $input['include_private'] ) ? 0 : 1; $output['include_attachments'] = empty( $input['include_attachments'] ) ? 0 : 1; $output['add_robots_reference'] = empty( $input['add_robots_reference'] ) ? 0 : 1; $output['safe_mode'] = empty( $input['safe_mode'] ) ? 0 : 1; $output['wizard_completed'] = empty( $input['wizard_completed'] ) ? 0 : 1; $output['report_branding'] = empty( $input['report_branding'] ) ? 0 : 1; $output['webmcp_enabled'] = empty( $input['webmcp_enabled'] ) ? 0 : 1; $output['webmcp_form_metadata_enabled'] = empty( $input['webmcp_form_metadata_enabled'] ) ? 0 : 1; $output['webmcp_search_form_enabled'] = empty( $input['webmcp_search_form_enabled'] ) ? 0 : 1; $output['webmcp_content_forms_enabled'] = empty( $input['webmcp_content_forms_enabled'] ) ? 0 : 1; $output['webmcp_confirmation_required'] = empty( $input['webmcp_confirmation_required'] ) ? 0 : 1; $output['agent_tools_enabled'] = empty( $input['agent_tools_enabled'] ) ? 0 : 1; $output['agent_tools_rest_enabled'] = empty( $input['agent_tools_rest_enabled'] ) ? 0 : 1; $output['agent_tools_search_enabled'] = empty( $input['agent_tools_search_enabled'] ) ? 0 : 1; $output['agent_tools_content_enabled'] = empty( $input['agent_tools_content_enabled'] ) ? 0 : 1; $output['agent_tools_recent_enabled'] = empty( $input['agent_tools_recent_enabled'] ) ? 0 : 1; $output['agent_tools_menus_enabled'] = empty( $input['agent_tools_menus_enabled'] ) ? 0 : 1; $output['agent_tools_taxonomies_enabled'] = empty( $input['agent_tools_taxonomies_enabled'] ) ? 0 : 1; $output['abilities_enabled'] = empty( $input['abilities_enabled'] ) ? 0 : 1; $output['abilities_public_enabled'] = empty( $input['abilities_public_enabled'] ) ? 0 : 1; $output['ai_profile_enabled'] = empty( $input['ai_profile_enabled'] ) ? 0 : 1; $output['site_health_enabled'] = empty( $input['site_health_enabled'] ) ? 0 : 1; $output['accessibility_scanner_enabled'] = empty( $input['accessibility_scanner_enabled'] ) ? 0 : 1; $output['layout_scanner_enabled'] = empty( $input['layout_scanner_enabled'] ) ? 0 : 1; $output['layout_helper_css_enabled'] = empty( $input['layout_helper_css_enabled'] ) ? 0 : 1; $output['layout_reserve_ad_slots'] = empty( $input['layout_reserve_ad_slots'] ) ? 0 : 1; $output['ecommerce_scanner_enabled'] = empty( $input['ecommerce_scanner_enabled'] ) ? 0 : 1; $output['ecommerce_product_tools_enabled'] = empty( $input['ecommerce_product_tools_enabled'] ) ? 0 : 1; $output['ecommerce_product_search_enabled'] = empty( $input['ecommerce_product_search_enabled'] ) ? 0 : 1; $output['ecommerce_product_details_enabled'] = empty( $input['ecommerce_product_details_enabled'] ) ? 0 : 1; $output['ecommerce_product_categories_enabled'] = empty( $input['ecommerce_product_categories_enabled'] ) ? 0 : 1; $output['ecommerce_include_out_of_stock'] = empty( $input['ecommerce_include_out_of_stock'] ) ? 0 : 1; $output['integrations_enabled'] = empty( $input['integrations_enabled'] ) ? 0 : 1; $output['integration_real_seo_master_enabled'] = empty( $input['integration_real_seo_master_enabled'] ) ? 0 : 1; $output['integration_yoast_enabled'] = empty( $input['integration_yoast_enabled'] ) ? 0 : 1; $output['integration_rankmath_enabled'] = empty( $input['integration_rankmath_enabled'] ) ? 0 : 1; $output['integration_aioseo_enabled'] = empty( $input['integration_aioseo_enabled'] ) ? 0 : 1; $output['integration_seopress_enabled'] = empty( $input['integration_seopress_enabled'] ) ? 0 : 1; $output['integration_wordpress_core_enabled'] = empty( $input['integration_wordpress_core_enabled'] ) ? 0 : 1; $output['integration_use_seo_descriptions_in_llms'] = empty( $input['integration_use_seo_descriptions_in_llms'] ) ? 0 : 1; $output['integration_expose_seo_metadata_in_tools'] = empty( $input['integration_expose_seo_metadata_in_tools'] ) ? 0 : 1; $output['integration_add_seo_notes_to_llms'] = empty( $input['integration_add_seo_notes_to_llms'] ) ? 0 : 1; $allowed_modes = array( 'auto', 'manual', 'hybrid' ); $mode = isset( $input['generation_mode'] ) ? sanitize_key( $input['generation_mode'] ) : $defaults['generation_mode']; $output['generation_mode'] = in_array( $mode, $allowed_modes, true ) ? $mode : $defaults['generation_mode']; $allowed_regen = array( 'manual', 'daily', 'weekly' ); $regen = isset( $input['regeneration'] ) ? sanitize_key( $input['regeneration'] ) : $defaults['regeneration']; $output['regeneration'] = in_array( $regen, $allowed_regen, true ) ? $regen : $defaults['regeneration']; $site_types = array_keys( $this->get_site_types() ); $site_type = isset( $input['site_type'] ) ? sanitize_key( $input['site_type'] ) : $defaults['site_type']; $output['site_type'] = in_array( $site_type, $site_types, true ) ? $site_type : $defaults['site_type']; $integration_sources = array( 'auto', 'real_seo_master', 'yoast', 'rank_math', 'aioseo', 'seopress', 'wordpress_core' ); $primary_source = isset( $input['integration_primary_source'] ) ? sanitize_key( $input['integration_primary_source'] ) : $defaults['integration_primary_source']; $output['integration_primary_source'] = in_array( $primary_source, $integration_sources, true ) ? $primary_source : $defaults['integration_primary_source']; $output['site_purpose'] = isset( $input['site_purpose'] ) ? sanitize_textarea_field( wp_unslash( $input['site_purpose'] ) ) : ''; $output['custom_intro'] = isset( $input['custom_intro'] ) ? sanitize_textarea_field( wp_unslash( $input['custom_intro'] ) ) : ''; $output['manual_llms'] = isset( $input['manual_llms'] ) ? $this->sanitize_markdown_text( $input['manual_llms'] ) : ''; $output['webmcp_tool_prefix'] = isset( $input['webmcp_tool_prefix'] ) ? $this->sanitize_tool_prefix( $input['webmcp_tool_prefix'] ) : $defaults['webmcp_tool_prefix']; $output['webmcp_default_form_description'] = isset( $input['webmcp_default_form_description'] ) ? sanitize_textarea_field( wp_unslash( $input['webmcp_default_form_description'] ) ) : $defaults['webmcp_default_form_description']; $output['include_post_types'] = $this->sanitize_key_array( isset( $input['include_post_types'] ) ? $input['include_post_types'] : array(), $this->get_public_post_type_names() ); $output['include_taxonomies'] = $this->sanitize_key_array( isset( $input['include_taxonomies'] ) ? $input['include_taxonomies'] : array(), $this->get_public_taxonomy_names() ); $output['max_items'] = $this->sanitize_int_range( isset( $input['max_items'] ) ? $input['max_items'] : $defaults['max_items'], 10, 500 ); $output['full_max_items'] = $this->sanitize_int_range( isset( $input['full_max_items'] ) ? $input['full_max_items'] : $defaults['full_max_items'], 50, 2000 ); $output['accessibility_scan_limit'] = $this->sanitize_int_range( isset( $input['accessibility_scan_limit'] ) ? $input['accessibility_scan_limit'] : $defaults['accessibility_scan_limit'], 1, 50 ); $output['layout_scan_limit'] = $this->sanitize_int_range( isset( $input['layout_scan_limit'] ) ? $input['layout_scan_limit'] : $defaults['layout_scan_limit'], 1, 50 ); $output['ecommerce_scan_limit'] = $this->sanitize_int_range( isset( $input['ecommerce_scan_limit'] ) ? $input['ecommerce_scan_limit'] : $defaults['ecommerce_scan_limit'], 1, 50 ); $output['integration_scan_limit'] = $this->sanitize_int_range( isset( $input['integration_scan_limit'] ) ? $input['integration_scan_limit'] : $defaults['integration_scan_limit'], 1, 50 ); $output['layout_top_ad_min_height'] = $this->sanitize_int_range( isset( $input['layout_top_ad_min_height'] ) ? $input['layout_top_ad_min_height'] : $defaults['layout_top_ad_min_height'], 0, 1000 ); $output['layout_inline_ad_min_height'] = $this->sanitize_int_range( isset( $input['layout_inline_ad_min_height'] ) ? $input['layout_inline_ad_min_height'] : $defaults['layout_inline_ad_min_height'], 0, 1200 ); $output['layout_sidebar_ad_min_height'] = $this->sanitize_int_range( isset( $input['layout_sidebar_ad_min_height'] ) ? $input['layout_sidebar_ad_min_height'] : $defaults['layout_sidebar_ad_min_height'], 0, 1200 ); $output['layout_mobile_ad_min_height'] = $this->sanitize_int_range( isset( $input['layout_mobile_ad_min_height'] ) ? $input['layout_mobile_ad_min_height'] : $defaults['layout_mobile_ad_min_height'], 0, 1000 ); $output['layout_sticky_header_offset'] = $this->sanitize_int_range( isset( $input['layout_sticky_header_offset'] ) ? $input['layout_sticky_header_offset'] : $defaults['layout_sticky_header_offset'], 0, 500 ); $output['last_generated'] = isset( $input['last_generated'] ) ? sanitize_text_field( wp_unslash( $input['last_generated'] ) ) : ''; $output['last_scan_score'] = isset( $input['last_scan_score'] ) ? $this->sanitize_int_range( $input['last_scan_score'], 0, 100 ) : 0; $output['last_scan_status'] = isset( $input['last_scan_status'] ) ? sanitize_key( $input['last_scan_status'] ) : ''; $output['last_scan_time'] = isset( $input['last_scan_time'] ) ? sanitize_text_field( wp_unslash( $input['last_scan_time'] ) ) : ''; return $output; } /** * Sanitize lightweight Markdown/plain text. * * @param mixed $text Input text. * @return string */ private function sanitize_markdown_text( $text ) { $text = is_string( $text ) ? wp_unslash( $text ) : ''; $text = wp_check_invalid_utf8( $text ); $text = str_replace( "\r\n", "\n", $text ); $text = str_replace( "\r", "\n", $text ); $text = wp_strip_all_tags( $text, false ); return trim( $text ); } /** * Sanitize WebMCP tool prefix. * * @param mixed $value Raw value. * @return string */ private function sanitize_tool_prefix( $value ) { $value = is_string( $value ) ? wp_unslash( $value ) : ''; $value = strtolower( $value ); $value = preg_replace( '/[^a-z0-9_]+/', '_', $value ); $value = preg_replace( '/_+/', '_', $value ); $value = trim( $value, '_' ); return '' !== $value ? substr( $value, 0, 32 ) : 'site'; } /** * Sanitize key array against allowed values. * * @param mixed $values Raw values. * @param array $allowed Allowed values. * @return array */ private function sanitize_key_array( $values, array $allowed ) { if ( ! is_array( $values ) ) { $values = array(); } $clean = array(); foreach ( $values as $value ) { $value = sanitize_key( $value ); if ( in_array( $value, $allowed, true ) ) { $clean[] = $value; } } return array_values( array_unique( $clean ) ); } /** * Sanitize integer range. * * @param mixed $value Raw value. * @param int $min Minimum. * @param int $max Maximum. * @return int */ private function sanitize_int_range( $value, $min, $max ) { $value = absint( $value ); if ( $value < $min ) { return $min; } if ( $value > $max ) { return $max; } return $value; } /** * Public post type names. * * @return array */ public function get_public_post_type_names() { $post_types = get_post_types( array( 'public' => true, ), 'names' ); unset( $post_types['attachment'] ); return array_values( $post_types ); } /** * Public taxonomies. * * @return array */ public function get_public_taxonomy_names() { $taxonomies = get_taxonomies( array( 'public' => true, ), 'names' ); unset( $taxonomies['nav_menu'] ); unset( $taxonomies['link_category'] ); unset( $taxonomies['post_format'] ); return array_values( $taxonomies ); } }