{"repo": "bcardi0427/website-ai-assistant", "files": {"admin/class-admin-settings.php": {"59d3ed1856b1051742d52a42048659abc04a9399": {"t": 1785745767.3724012, "n": 50772, "h": "9a4b86061dd9a6a3", "c": " $options['ai_provider'] ?? 'not set',\n 'model' => $options[$options['ai_provider'] . '_model'] ?? 'not set',\n 'has_api_key' => !empty($options[$options['ai_provider'] . '_api_key']) ? 'yes' : 'no'\n ]);\n });\n }\n\n public function add_settings_page(): void {\n add_options_page(\n __('Website AI Assistant Settings', 'website-ai-assistant'),\n __('Website AI Assistant', 'website-ai-assistant'),\n 'manage_options',\n self::SETTINGS_PAGE,\n [$this, 'render_settings_page']\n );\n }\npublic function render_text_field($args): void {\n if (empty($args['field'])) {\n waa_debug_log(\"Error: Missing field name in render_text_field\", $args);\n return;\n }\n\n $options = get_option('waa_options', []);\n $field_name = $args['field'];\n $field_type = $args['type'] ?? 'text';\n $value = $options[$field_name] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n waa_debug_log(\"Rendering field: $field_name\", [\n 'type' => $field_type,\n 'value' => $value,\n 'class' => $class\n ]);\n \n ?>\n
' . esc_html($args['description']) . '
';\n }\n}\n\npublic function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n // Build data attributes\n $data_attrs = '';\n if (!empty($args['data'])) {\n foreach ($args['data'] as $key => $val) {\n $data_attrs .= ' data-' . esc_attr($key) . '=\"' . esc_attr($val) . '\"';\n }\n }\n \n $id = $args['id'] ?? 'waa_options_' . esc_attr($field);\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n}\n\npublic function register_settings(): void {\n // Register the settings group\n register_setting(\n 'waa_options', // Option group\n 'waa_options', // Option name in the database\n ['sanitize_callback' => [$this, 'sanitize_options']]\n );\n\n // Register settings sections\n $this->register_ai_settings(); // AI provider and model settings\n $this->register_api_settings(); // API credentials for all providers\n $this->register_search_settings(); // Search configuration\n $this->register_display_settings(); // Display options\n $this->register_lead_settings(); // Lead generation settings\n $this->register_consent_settings(); // Consent settings\n\n // Log registered settings for debugging\n waa_debug_log('Settings registered with the following data:', [\n 'option_group' => 'waa_options',\n 'sections' => [\n 'ai_settings' => 'website-ai-assistant',\n 'api_credentials' => 'website-ai-assistant',\n 'search_settings' => 'website-ai-assistant',\n 'display_settings' => 'website-ai-assistant',\n 'lead_settings' => 'website-ai-assistant',\n 'consent_settings' => 'website-ai-assistant'\n ]\n ]);\n}\n\nprivate function register_lead_settings(): void {\n add_settings_section(\n 'waa_lead_settings',\n __('Lead Generation Settings', 'website-ai-assistant'),\n [$this, 'render_lead_section'],\n 'website-ai-assistant'\n );\n\n // Enable lead collection\n add_settings_field(\n 'enable_lead_collection',\n __('Enable Lead Collection', 'website-ai-assistant'),\n [$this, 'render_checkbox_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'enable_lead_collection',\n 'description' => __('Show lead collection form in chat widget', 'website-ai-assistant')\n ]\n );\n\n // Lead collection timing\n add_settings_field(\n 'lead_collection_timing',\n __('When to Show Form', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_timing',\n 'options' => [\n 'immediate' => __('Immediately', 'website-ai-assistant'),\n 'after_first' => __('After First Message', 'website-ai-assistant'),\n 'after_two' => __('After Two Messages', 'website-ai-assistant'),\n 'end' => __('After Three Messages', 'website-ai-assistant')\n ],\n 'default' => 'immediate',\n 'description' => __('Choose when to display the lead collection form', 'website-ai-assistant')\n ]\n );\n\n // Form heading\n add_settings_field(\n 'lead_collection_heading',\n __('Form Heading', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_heading',\n 'type' => 'text',\n 'default' => __('Please share your contact info to continue', 'website-ai-assistant'),\n 'description' => __('Heading text displayed at the top of the form', 'website-ai-assistant')\n ]\n );\n\n // Form description\n add_settings_field(\n 'lead_collection_description',\n __('Form Description', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_description',\n 'type' => 'text',\n 'description' => __('Optional description text below the heading', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM List selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_list_id',\n __('FluentCRM List', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_list_id',\n 'options' => $this->get_fluentcrm_lists(),\n 'description' => __('Select the FluentCRM list to add contacts to', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM Tag selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_tag_id',\n __('FluentCRM Tag', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_tag_id',\n 'options' => $this->get_fluentcrm_tags(),\n 'description' => __('Select the FluentCRM tag to apply to contacts', 'website-ai-assistant')\n ]\n );\n\n // Contact Status\n add_settings_field(\n 'fluentcrm_status',\n __('Contact Status', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_status',\n 'options' => [\n 'subscribed' => __('Subscribed', 'website-ai-assistant'),\n 'pending' => __('Pending', 'website-ai-assistant'),\n 'unsubscribed' => __('Unsubscribed', 'website-ai-assistant')\n ],\n 'default' => 'subscribed',\n 'description' => __('Select the default status for new contacts', 'website-ai-assistant')\n ]\n );\n}\n\npublic function render_lead_section(): void {\n echo '' . esc_html__('Configure lead generation settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure chatbot consent settings below:', 'website-ai-assistant') . '
';\n }\n\n private function get_ai_provider_options(): array {\n return [\n 'gemini' => __('Google Gemini AI', 'website-ai-assistant'),\n 'openai' => __('OpenAI', 'website-ai-assistant'),\n 'deepseek' => __('Deepseek', 'website-ai-assistant')\n ];\n }\n\n private function get_fluentcrm_lists(): array {\n if (!function_exists('FluentCrmApi')) {\n return ['' => __('FluentCRM not installed', 'website-ai-assistant')];\n }\n\n try {\n $listApi = FluentCrmApi('lists');\n if (!$listApi) {\n return ['' => __('FluentCRM API not available', 'website-ai-assistant')];\n }\n\n $lists = $listApi->all();\n if (empty($lists)) {\n return ['' => __('No lists found', 'website-ai-assistant')];\n }\n\n $options = ['' => __('Select a list', 'website-ai-assistant')];\n foreach ($lists as $list) {\n $options[$list->id] = esc_html($list->title);\n }\n return $options;\n } catch (\\Exception $e) {\n error_log('Website AI Assistant Error: ' . $e->getMessage());\n return ['' => __('Error loading lists', 'website-ai-assistant')];\n }\n }\n\n private function get_fluentcrm_tags(): array {\n if (!function_exists('FluentCrmApi')) {\n return ['' => __('FluentCRM not installed', 'website-ai-assistant')];\n }\n\n try {\n $tagApi = FluentCrmApi('tags');\n if (!$tagApi) {\n return ['' => __('FluentCRM API not available', 'website-ai-assistant')];\n }\n\n $tags = $tagApi->all();\n if (empty($tags)) {\n return ['' => __('No tags found', 'website-ai-assistant')];\n }\n\n $options = ['' => __('Select a tag', 'website-ai-assistant')];\n foreach ($tags as $tag) {\n $options[$tag->id] = esc_html($tag->title);\n }\n return $options;\n } catch (\\Exception $e) {\n error_log('Website AI Assistant Error: ' . $e->getMessage());\n return ['' => __('Error loading tags', 'website-ai-assistant')];\n }\n }\n\n public function render_settings_page(): void {\n if (!current_user_can('manage_options')) {\n return;\n }\n require_once WAA_PLUGIN_DIR . 'admin/views/settings-page.php';\n }\n\n public function render_ai_section(): void {\n echo '' . esc_html__('Configure your AI settings below:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('API Credentials:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Search Provider Settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure where the chat widget should appear.', 'website-ai-assistant') . '
';\n }\n\n\n /* Commented out duplicate method - preserved for reference\n public function render_textarea_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? '';\n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function handle_fetch_models(): void {\n error_log('Starting handle_fetch_models...');\n check_ajax_referer('waa_admin_nonce', 'nonce');\n \n if (!current_user_can('manage_options')) {\n error_log('Unauthorized access attempt in handle_fetch_models');\n wp_send_json_error(['message' => __('Unauthorized access', 'website-ai-assistant')]);\n return;\n }\n \n $provider = sanitize_text_field($_POST['provider'] ?? '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('handle_fetch_models called', [\n 'provider' => $provider,\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'post_data' => $_POST,\n 'request_method' => $_SERVER['REQUEST_METHOD']\n ]);\n \n $models = [];\n \n switch ($provider) {\n case 'gemini':\n $models = \\Website_Ai_Assistant\\Models\\Gemini_Service::AVAILABLE_MODELS;\n update_option('waa_gemini_models', $models);\n waa_debug_log('Using static Gemini models');\n break;\n case 'openai':\n try {\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('OpenAI: Processing model fetch request');\n \n waa_debug_log('OpenAI: Processing fetch models request', [\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'api_key_preview' => substr($api_key, 0, 10) . '...'\n ]);\n\n // Check for cached models first\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n \n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n \n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n \n // If no cache hit, determine whether to use cache for fetching\n $options = get_option('waa_options', []);\n $force_refresh = isset($_POST['force_refresh']) && $_POST['force_refresh'] === 'true';\n $use_cache = !$force_refresh && empty($options['openai_disable_cache']);\n\n waa_debug_log('OpenAI: Cache Settings:', [\n 'use_cache' => $use_cache,\n 'force_refresh' => $force_refresh ? 'yes' : 'no',\n 'disable_cache_setting' => !empty($options['openai_disable_cache']) ? 'yes' : 'no'\n ]);\n \n if ($use_cache) {\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n\n waa_debug_log('OpenAI: Cache status', [\n 'has_cached_models' => !empty($cached_models),\n 'models_count' => count($cached_models),\n 'cache_key_match' => $cached_key_hash === $current_key_hash,\n 'current_key_hash' => $current_key_hash\n ]);\n\n // Use cached models if available and key matches\n if ($cached_models && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n }\n \n // If we get here, either cache is disabled or we need fresh models\n waa_debug_log('OpenAI: Fetching fresh models from API');\n $openai_service = new OpenAI_Service($api_key);\n $models = $openai_service->get_available_models();\n \n // Cache the models unless this was a forced refresh\n if (!$force_refresh && !empty($models)) {\n update_option('waa_openai_models', $models);\n update_option('waa_openai_key_hash', md5($api_key));\n waa_debug_log('OpenAI: Cached new models');\n }\n\n // Set default model if none selected\n if (!empty($models)) {\n $options = get_option('waa_options', []);\n if (empty($options['openai_model'])) {\n $first_model = array_key_first($models);\n $options['openai_model'] = $first_model;\n update_option('waa_options', $options);\n waa_debug_log('OpenAI: Set default model:', $first_model);\n }\n }\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n case 'deepseek':\n // Check for cached models first\n $cached_models = get_option('waa_deepseek_models', []);\n $cached_key_hash = get_option('waa_deepseek_key_hash', '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n $current_key_hash = md5($api_key);\n\n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('Deepseek: Using cached models');\n break;\n }\n\n // If no cache hit, validate API key and fetch models\n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n\n try {\n $deepseek_service = new Deepseek_Service($api_key, Deepseek_Service::API_ENDPOINT);\n $models = $deepseek_service->get_available_models();\n \n // Cache the models and API key hash\n update_option('waa_deepseek_models', $models);\n update_option('waa_deepseek_key_hash', $current_key_hash);\n waa_debug_log('Deepseek: Cached new models');\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n default:\n wp_send_json_error(['message' => __('Invalid provider', 'website-ai-assistant')]);\n return;\n }\n \n wp_send_json_success(['models' => $models]);\n }\n\n /* Commented out duplicate method - preserved for reference\n public function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function render_checkbox_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $checked = isset($options[$field]) && $options[$field] ? 'checked' : '';\n \n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n\n public function render_display_locations_field(): void {\n $options = get_option('waa_options', []);\n $locations = $options['display_locations'] ?? ['all'];\n ?>\n \n admin_url('admin-ajax.php'),\n 'nonce' => wp_create_nonce('waa_admin_nonce'),\n 'i18n' => [\n 'loadingModels' => __('Loading available models...', 'website-ai-assistant'),\n 'enterApiKey' => __('Please enter API key first', 'website-ai-assistant'),\n 'selectModel' => __('Select a model', 'website-ai-assistant'),\n 'fetchError' => __('Failed to fetch models', 'website-ai-assistant'),\n 'noModelsFound' => __('No available models found', 'website-ai-assistant'),\n 'fetchingModels' => __('Fetching available models...', 'website-ai-assistant')\n ]\n ]);\n }\n\n public function sanitize_options($input): array {\n waa_debug_log('Sanitizing input options:', [\n 'ai_provider' => $input['ai_provider'] ?? 'not set',\n 'search_provider' => $input['search_provider'] ?? 'not set',\n 'fields_present' => array_keys($input)\n ]);\n\n // Get existing options to preserve any values not in current input\n $existing = get_option('waa_options', []);\n $sanitized = $existing;\n\n // Handle search provider changes\n if (isset($input['search_provider'])) {\n $search_provider = sanitize_text_field($input['search_provider']);\n if (in_array($search_provider, ['google', 'algolia'])) {\n $old_provider = $existing['search_provider'] ?? 'google';\n $sanitized['search_provider'] = $search_provider;\n \n waa_debug_log('Search provider updated:', [\n 'from' => $old_provider,\n 'to' => $search_provider\n ]);\n\n // Only clear settings if they're explicitly empty in the input\n if ($search_provider === 'google') {\n // When switching to Google, preserve existing Algolia settings\n $sanitized['google_search_api_key'] = $input['google_search_api_key'] ?? $existing['google_search_api_key'] ?? '';\n $sanitized['search_engine_id'] = $input['search_engine_id'] ?? $existing['search_engine_id'] ?? '';\n } else if ($search_provider === 'algolia') {\n // When switching to Algolia, preserve existing Google settings\n $sanitized['algolia_app_id'] = $input['algolia_app_id'] ?? $existing['algolia_app_id'] ?? '';\n $sanitized['algolia_search_key'] = $input['algolia_search_key'] ?? $existing['algolia_search_key'] ?? '';\n $sanitized['algolia_admin_key'] = $input['algolia_admin_key'] ?? $existing['algolia_admin_key'] ?? '';\n $sanitized['algolia_index'] = $input['algolia_index'] ?? $existing['algolia_index'] ?? '';\n }\n \n waa_debug_log('Settings after provider change:', [\n 'provider' => $search_provider,\n 'has_google_key' => isset($sanitized['google_search_api_key']),\n 'has_algolia_key' => isset($sanitized['algolia_search_key'])\n ]);\n }\n }\n \n $field_types = [\n 'url' => ['deepseek_endpoint'],\n 'key' => [\n 'gemini_api_key', 'openai_api_key', 'deepseek_api_key',\n 'google_search_api_key', 'search_engine_id',\n 'algolia_app_id', 'algolia_search_key', 'algolia_admin_key'\n ],\n 'text' => ['algolia_index']\n ];\n\n // Process each field type\n foreach ($field_types as $type => $fields) {\n foreach ($fields as $field) {\n if (!isset($input[$field])) continue;\n\n switch ($type) {\n case 'url':\n $sanitized[$field] = esc_url_raw(rtrim($input[$field], '/') . '/');\n break;\n case 'key':\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n if ($type === 'text') {\n waa_debug_log(\"Sanitizing text field: $field\", [\n 'input_value' => $input[$field],\n 'sanitized_value' => $sanitized[$field]\n ]);\n }\n break;\n }\n }\n }\n\n // Provider and model fields\n $provider_model_fields = [\n 'ai_provider',\n 'openai_model',\n 'gemini_model',\n 'deepseek_model',\n 'search_provider',\n 'system_message'\n ];\n\n foreach ($provider_model_fields as $field) {\n if (isset($input[$field])) {\n $sanitized[$field] = sanitize_text_field($input[$field]);\n waa_debug_log(\"Sanitized $field:\", $sanitized[$field]);\n } else {\n waa_debug_log(\"Field not found in input: $field\");\n $sanitized[$field] = '';\n }\n }\n\n // Add special handling for provider-specific model fields\n $provider = $sanitized['ai_provider'] ?? '';\n if ($provider) {\n $model_field = $provider . '_model';\n waa_debug_log(\"Checking provider-specific model field: $model_field\", [\n 'value' => $sanitized[$model_field] ?? 'not set',\n 'provider' => $provider\n ]);\n }\n\n // Handle display locations array\n if (isset($input['display_locations']) && is_array($input['display_locations'])) {\n $sanitized['display_locations'] = array_map('sanitize_text_field', $input['display_locations']);\n } else {\n $sanitized['display_locations'] = ['all']; // Default to all pages if not set\n }\n\n // Handle debug option\n $sanitized['enable_debug'] = isset($input['enable_debug']) ? 1 : 0;\n\n // Sanitize lead generation fields\n $lead_fields = [\n 'enable_lead_collection' => 'bool',\n 'lead_collection_timing' => 'text',\n 'lead_collection_heading' => 'text',\n 'lead_collection_description' => 'text',\n 'fluentcrm_list_id' => 'int',\n 'fluentcrm_tag_id' => 'int',\n 'fluentcrm_status' => 'text'\n ];\n\n foreach ($lead_fields as $field => $type) {\n if (isset($input[$field])) {\n switch ($type) {\n case 'bool':\n $sanitized[$field] = (bool) $input[$field];\n break;\n case 'int':\n $sanitized[$field] = absint($input[$field]);\n break;\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n break;\n }\n }\n }\n\n // Sanitize consent settings\n $sanitized['enable_consent'] = isset($input['enable_consent']) ? 1 : 0;\n $sanitized['consent_message'] = isset($input['consent_message']) ? sanitize_textarea_field($input['consent_message']) : '';\n\n return $sanitized;\n }\n\n}\n"}, "424bfbab1d94a7b7783b5bb6012f882c30a08c45": {"t": 1785756317.915637, "n": 50772, "h": "9a4b86061dd9a6a3", "c": " $options['ai_provider'] ?? 'not set',\n 'model' => $options[$options['ai_provider'] . '_model'] ?? 'not set',\n 'has_api_key' => !empty($options[$options['ai_provider'] . '_api_key']) ? 'yes' : 'no'\n ]);\n });\n }\n\n public function add_settings_page(): void {\n add_options_page(\n __('Website AI Assistant Settings', 'website-ai-assistant'),\n __('Website AI Assistant', 'website-ai-assistant'),\n 'manage_options',\n self::SETTINGS_PAGE,\n [$this, 'render_settings_page']\n );\n }\npublic function render_text_field($args): void {\n if (empty($args['field'])) {\n waa_debug_log(\"Error: Missing field name in render_text_field\", $args);\n return;\n }\n\n $options = get_option('waa_options', []);\n $field_name = $args['field'];\n $field_type = $args['type'] ?? 'text';\n $value = $options[$field_name] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n waa_debug_log(\"Rendering field: $field_name\", [\n 'type' => $field_type,\n 'value' => $value,\n 'class' => $class\n ]);\n \n ?>\n' . esc_html($args['description']) . '
';\n }\n}\n\npublic function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n // Build data attributes\n $data_attrs = '';\n if (!empty($args['data'])) {\n foreach ($args['data'] as $key => $val) {\n $data_attrs .= ' data-' . esc_attr($key) . '=\"' . esc_attr($val) . '\"';\n }\n }\n \n $id = $args['id'] ?? 'waa_options_' . esc_attr($field);\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n}\n\npublic function register_settings(): void {\n // Register the settings group\n register_setting(\n 'waa_options', // Option group\n 'waa_options', // Option name in the database\n ['sanitize_callback' => [$this, 'sanitize_options']]\n );\n\n // Register settings sections\n $this->register_ai_settings(); // AI provider and model settings\n $this->register_api_settings(); // API credentials for all providers\n $this->register_search_settings(); // Search configuration\n $this->register_display_settings(); // Display options\n $this->register_lead_settings(); // Lead generation settings\n $this->register_consent_settings(); // Consent settings\n\n // Log registered settings for debugging\n waa_debug_log('Settings registered with the following data:', [\n 'option_group' => 'waa_options',\n 'sections' => [\n 'ai_settings' => 'website-ai-assistant',\n 'api_credentials' => 'website-ai-assistant',\n 'search_settings' => 'website-ai-assistant',\n 'display_settings' => 'website-ai-assistant',\n 'lead_settings' => 'website-ai-assistant',\n 'consent_settings' => 'website-ai-assistant'\n ]\n ]);\n}\n\nprivate function register_lead_settings(): void {\n add_settings_section(\n 'waa_lead_settings',\n __('Lead Generation Settings', 'website-ai-assistant'),\n [$this, 'render_lead_section'],\n 'website-ai-assistant'\n );\n\n // Enable lead collection\n add_settings_field(\n 'enable_lead_collection',\n __('Enable Lead Collection', 'website-ai-assistant'),\n [$this, 'render_checkbox_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'enable_lead_collection',\n 'description' => __('Show lead collection form in chat widget', 'website-ai-assistant')\n ]\n );\n\n // Lead collection timing\n add_settings_field(\n 'lead_collection_timing',\n __('When to Show Form', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_timing',\n 'options' => [\n 'immediate' => __('Immediately', 'website-ai-assistant'),\n 'after_first' => __('After First Message', 'website-ai-assistant'),\n 'after_two' => __('After Two Messages', 'website-ai-assistant'),\n 'end' => __('After Three Messages', 'website-ai-assistant')\n ],\n 'default' => 'immediate',\n 'description' => __('Choose when to display the lead collection form', 'website-ai-assistant')\n ]\n );\n\n // Form heading\n add_settings_field(\n 'lead_collection_heading',\n __('Form Heading', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_heading',\n 'type' => 'text',\n 'default' => __('Please share your contact info to continue', 'website-ai-assistant'),\n 'description' => __('Heading text displayed at the top of the form', 'website-ai-assistant')\n ]\n );\n\n // Form description\n add_settings_field(\n 'lead_collection_description',\n __('Form Description', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_description',\n 'type' => 'text',\n 'description' => __('Optional description text below the heading', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM List selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_list_id',\n __('FluentCRM List', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_list_id',\n 'options' => $this->get_fluentcrm_lists(),\n 'description' => __('Select the FluentCRM list to add contacts to', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM Tag selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_tag_id',\n __('FluentCRM Tag', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_tag_id',\n 'options' => $this->get_fluentcrm_tags(),\n 'description' => __('Select the FluentCRM tag to apply to contacts', 'website-ai-assistant')\n ]\n );\n\n // Contact Status\n add_settings_field(\n 'fluentcrm_status',\n __('Contact Status', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_status',\n 'options' => [\n 'subscribed' => __('Subscribed', 'website-ai-assistant'),\n 'pending' => __('Pending', 'website-ai-assistant'),\n 'unsubscribed' => __('Unsubscribed', 'website-ai-assistant')\n ],\n 'default' => 'subscribed',\n 'description' => __('Select the default status for new contacts', 'website-ai-assistant')\n ]\n );\n}\n\npublic function render_lead_section(): void {\n echo '' . esc_html__('Configure lead generation settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure chatbot consent settings below:', 'website-ai-assistant') . '
';\n }\n\n private function get_ai_provider_options(): array {\n return [\n 'gemini' => __('Google Gemini AI', 'website-ai-assistant'),\n 'openai' => __('OpenAI', 'website-ai-assistant'),\n 'deepseek' => __('Deepseek', 'website-ai-assistant')\n ];\n }\n\n private function get_fluentcrm_lists(): array {\n if (!function_exists('FluentCrmApi')) {\n return ['' => __('FluentCRM not installed', 'website-ai-assistant')];\n }\n\n try {\n $listApi = FluentCrmApi('lists');\n if (!$listApi) {\n return ['' => __('FluentCRM API not available', 'website-ai-assistant')];\n }\n\n $lists = $listApi->all();\n if (empty($lists)) {\n return ['' => __('No lists found', 'website-ai-assistant')];\n }\n\n $options = ['' => __('Select a list', 'website-ai-assistant')];\n foreach ($lists as $list) {\n $options[$list->id] = esc_html($list->title);\n }\n return $options;\n } catch (\\Exception $e) {\n error_log('Website AI Assistant Error: ' . $e->getMessage());\n return ['' => __('Error loading lists', 'website-ai-assistant')];\n }\n }\n\n private function get_fluentcrm_tags(): array {\n if (!function_exists('FluentCrmApi')) {\n return ['' => __('FluentCRM not installed', 'website-ai-assistant')];\n }\n\n try {\n $tagApi = FluentCrmApi('tags');\n if (!$tagApi) {\n return ['' => __('FluentCRM API not available', 'website-ai-assistant')];\n }\n\n $tags = $tagApi->all();\n if (empty($tags)) {\n return ['' => __('No tags found', 'website-ai-assistant')];\n }\n\n $options = ['' => __('Select a tag', 'website-ai-assistant')];\n foreach ($tags as $tag) {\n $options[$tag->id] = esc_html($tag->title);\n }\n return $options;\n } catch (\\Exception $e) {\n error_log('Website AI Assistant Error: ' . $e->getMessage());\n return ['' => __('Error loading tags', 'website-ai-assistant')];\n }\n }\n\n public function render_settings_page(): void {\n if (!current_user_can('manage_options')) {\n return;\n }\n require_once WAA_PLUGIN_DIR . 'admin/views/settings-page.php';\n }\n\n public function render_ai_section(): void {\n echo '' . esc_html__('Configure your AI settings below:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('API Credentials:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Search Provider Settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure where the chat widget should appear.', 'website-ai-assistant') . '
';\n }\n\n\n /* Commented out duplicate method - preserved for reference\n public function render_textarea_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? '';\n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function handle_fetch_models(): void {\n error_log('Starting handle_fetch_models...');\n check_ajax_referer('waa_admin_nonce', 'nonce');\n \n if (!current_user_can('manage_options')) {\n error_log('Unauthorized access attempt in handle_fetch_models');\n wp_send_json_error(['message' => __('Unauthorized access', 'website-ai-assistant')]);\n return;\n }\n \n $provider = sanitize_text_field($_POST['provider'] ?? '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('handle_fetch_models called', [\n 'provider' => $provider,\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'post_data' => $_POST,\n 'request_method' => $_SERVER['REQUEST_METHOD']\n ]);\n \n $models = [];\n \n switch ($provider) {\n case 'gemini':\n $models = \\Website_Ai_Assistant\\Models\\Gemini_Service::AVAILABLE_MODELS;\n update_option('waa_gemini_models', $models);\n waa_debug_log('Using static Gemini models');\n break;\n case 'openai':\n try {\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('OpenAI: Processing model fetch request');\n \n waa_debug_log('OpenAI: Processing fetch models request', [\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'api_key_preview' => substr($api_key, 0, 10) . '...'\n ]);\n\n // Check for cached models first\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n \n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n \n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n \n // If no cache hit, determine whether to use cache for fetching\n $options = get_option('waa_options', []);\n $force_refresh = isset($_POST['force_refresh']) && $_POST['force_refresh'] === 'true';\n $use_cache = !$force_refresh && empty($options['openai_disable_cache']);\n\n waa_debug_log('OpenAI: Cache Settings:', [\n 'use_cache' => $use_cache,\n 'force_refresh' => $force_refresh ? 'yes' : 'no',\n 'disable_cache_setting' => !empty($options['openai_disable_cache']) ? 'yes' : 'no'\n ]);\n \n if ($use_cache) {\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n\n waa_debug_log('OpenAI: Cache status', [\n 'has_cached_models' => !empty($cached_models),\n 'models_count' => count($cached_models),\n 'cache_key_match' => $cached_key_hash === $current_key_hash,\n 'current_key_hash' => $current_key_hash\n ]);\n\n // Use cached models if available and key matches\n if ($cached_models && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n }\n \n // If we get here, either cache is disabled or we need fresh models\n waa_debug_log('OpenAI: Fetching fresh models from API');\n $openai_service = new OpenAI_Service($api_key);\n $models = $openai_service->get_available_models();\n \n // Cache the models unless this was a forced refresh\n if (!$force_refresh && !empty($models)) {\n update_option('waa_openai_models', $models);\n update_option('waa_openai_key_hash', md5($api_key));\n waa_debug_log('OpenAI: Cached new models');\n }\n\n // Set default model if none selected\n if (!empty($models)) {\n $options = get_option('waa_options', []);\n if (empty($options['openai_model'])) {\n $first_model = array_key_first($models);\n $options['openai_model'] = $first_model;\n update_option('waa_options', $options);\n waa_debug_log('OpenAI: Set default model:', $first_model);\n }\n }\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n case 'deepseek':\n // Check for cached models first\n $cached_models = get_option('waa_deepseek_models', []);\n $cached_key_hash = get_option('waa_deepseek_key_hash', '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n $current_key_hash = md5($api_key);\n\n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('Deepseek: Using cached models');\n break;\n }\n\n // If no cache hit, validate API key and fetch models\n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n\n try {\n $deepseek_service = new Deepseek_Service($api_key, Deepseek_Service::API_ENDPOINT);\n $models = $deepseek_service->get_available_models();\n \n // Cache the models and API key hash\n update_option('waa_deepseek_models', $models);\n update_option('waa_deepseek_key_hash', $current_key_hash);\n waa_debug_log('Deepseek: Cached new models');\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n default:\n wp_send_json_error(['message' => __('Invalid provider', 'website-ai-assistant')]);\n return;\n }\n \n wp_send_json_success(['models' => $models]);\n }\n\n /* Commented out duplicate method - preserved for reference\n public function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function render_checkbox_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $checked = isset($options[$field]) && $options[$field] ? 'checked' : '';\n \n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n\n public function render_display_locations_field(): void {\n $options = get_option('waa_options', []);\n $locations = $options['display_locations'] ?? ['all'];\n ?>\n \n admin_url('admin-ajax.php'),\n 'nonce' => wp_create_nonce('waa_admin_nonce'),\n 'i18n' => [\n 'loadingModels' => __('Loading available models...', 'website-ai-assistant'),\n 'enterApiKey' => __('Please enter API key first', 'website-ai-assistant'),\n 'selectModel' => __('Select a model', 'website-ai-assistant'),\n 'fetchError' => __('Failed to fetch models', 'website-ai-assistant'),\n 'noModelsFound' => __('No available models found', 'website-ai-assistant'),\n 'fetchingModels' => __('Fetching available models...', 'website-ai-assistant')\n ]\n ]);\n }\n\n public function sanitize_options($input): array {\n waa_debug_log('Sanitizing input options:', [\n 'ai_provider' => $input['ai_provider'] ?? 'not set',\n 'search_provider' => $input['search_provider'] ?? 'not set',\n 'fields_present' => array_keys($input)\n ]);\n\n // Get existing options to preserve any values not in current input\n $existing = get_option('waa_options', []);\n $sanitized = $existing;\n\n // Handle search provider changes\n if (isset($input['search_provider'])) {\n $search_provider = sanitize_text_field($input['search_provider']);\n if (in_array($search_provider, ['google', 'algolia'])) {\n $old_provider = $existing['search_provider'] ?? 'google';\n $sanitized['search_provider'] = $search_provider;\n \n waa_debug_log('Search provider updated:', [\n 'from' => $old_provider,\n 'to' => $search_provider\n ]);\n\n // Only clear settings if they're explicitly empty in the input\n if ($search_provider === 'google') {\n // When switching to Google, preserve existing Algolia settings\n $sanitized['google_search_api_key'] = $input['google_search_api_key'] ?? $existing['google_search_api_key'] ?? '';\n $sanitized['search_engine_id'] = $input['search_engine_id'] ?? $existing['search_engine_id'] ?? '';\n } else if ($search_provider === 'algolia') {\n // When switching to Algolia, preserve existing Google settings\n $sanitized['algolia_app_id'] = $input['algolia_app_id'] ?? $existing['algolia_app_id'] ?? '';\n $sanitized['algolia_search_key'] = $input['algolia_search_key'] ?? $existing['algolia_search_key'] ?? '';\n $sanitized['algolia_admin_key'] = $input['algolia_admin_key'] ?? $existing['algolia_admin_key'] ?? '';\n $sanitized['algolia_index'] = $input['algolia_index'] ?? $existing['algolia_index'] ?? '';\n }\n \n waa_debug_log('Settings after provider change:', [\n 'provider' => $search_provider,\n 'has_google_key' => isset($sanitized['google_search_api_key']),\n 'has_algolia_key' => isset($sanitized['algolia_search_key'])\n ]);\n }\n }\n \n $field_types = [\n 'url' => ['deepseek_endpoint'],\n 'key' => [\n 'gemini_api_key', 'openai_api_key', 'deepseek_api_key',\n 'google_search_api_key', 'search_engine_id',\n 'algolia_app_id', 'algolia_search_key', 'algolia_admin_key'\n ],\n 'text' => ['algolia_index']\n ];\n\n // Process each field type\n foreach ($field_types as $type => $fields) {\n foreach ($fields as $field) {\n if (!isset($input[$field])) continue;\n\n switch ($type) {\n case 'url':\n $sanitized[$field] = esc_url_raw(rtrim($input[$field], '/') . '/');\n break;\n case 'key':\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n if ($type === 'text') {\n waa_debug_log(\"Sanitizing text field: $field\", [\n 'input_value' => $input[$field],\n 'sanitized_value' => $sanitized[$field]\n ]);\n }\n break;\n }\n }\n }\n\n // Provider and model fields\n $provider_model_fields = [\n 'ai_provider',\n 'openai_model',\n 'gemini_model',\n 'deepseek_model',\n 'search_provider',\n 'system_message'\n ];\n\n foreach ($provider_model_fields as $field) {\n if (isset($input[$field])) {\n $sanitized[$field] = sanitize_text_field($input[$field]);\n waa_debug_log(\"Sanitized $field:\", $sanitized[$field]);\n } else {\n waa_debug_log(\"Field not found in input: $field\");\n $sanitized[$field] = '';\n }\n }\n\n // Add special handling for provider-specific model fields\n $provider = $sanitized['ai_provider'] ?? '';\n if ($provider) {\n $model_field = $provider . '_model';\n waa_debug_log(\"Checking provider-specific model field: $model_field\", [\n 'value' => $sanitized[$model_field] ?? 'not set',\n 'provider' => $provider\n ]);\n }\n\n // Handle display locations array\n if (isset($input['display_locations']) && is_array($input['display_locations'])) {\n $sanitized['display_locations'] = array_map('sanitize_text_field', $input['display_locations']);\n } else {\n $sanitized['display_locations'] = ['all']; // Default to all pages if not set\n }\n\n // Handle debug option\n $sanitized['enable_debug'] = isset($input['enable_debug']) ? 1 : 0;\n\n // Sanitize lead generation fields\n $lead_fields = [\n 'enable_lead_collection' => 'bool',\n 'lead_collection_timing' => 'text',\n 'lead_collection_heading' => 'text',\n 'lead_collection_description' => 'text',\n 'fluentcrm_list_id' => 'int',\n 'fluentcrm_tag_id' => 'int',\n 'fluentcrm_status' => 'text'\n ];\n\n foreach ($lead_fields as $field => $type) {\n if (isset($input[$field])) {\n switch ($type) {\n case 'bool':\n $sanitized[$field] = (bool) $input[$field];\n break;\n case 'int':\n $sanitized[$field] = absint($input[$field]);\n break;\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n break;\n }\n }\n }\n\n // Sanitize consent settings\n $sanitized['enable_consent'] = isset($input['enable_consent']) ? 1 : 0;\n $sanitized['consent_message'] = isset($input['consent_message']) ? sanitize_textarea_field($input['consent_message']) : '';\n\n return $sanitized;\n }\n\n}\n"}, "c52f8b402a1362aecc23312ba13ae6e5a11fb792": {"t": 1785756319.694695, "n": 48986, "h": "d948add6048bc0cb", "c": " $options['ai_provider'] ?? 'not set',\n 'model' => $options[$options['ai_provider'] . '_model'] ?? 'not set',\n 'has_api_key' => !empty($options[$options['ai_provider'] . '_api_key']) ? 'yes' : 'no'\n ]);\n });\n }\n\n public function add_settings_page(): void {\n add_options_page(\n __('Website AI Assistant Settings', 'website-ai-assistant'),\n __('Website AI Assistant', 'website-ai-assistant'),\n 'manage_options',\n self::SETTINGS_PAGE,\n [$this, 'render_settings_page']\n );\n }\npublic function render_text_field($args): void {\n if (empty($args['field'])) {\n waa_debug_log(\"Error: Missing field name in render_text_field\", $args);\n return;\n }\n\n $options = get_option('waa_options', []);\n $field_name = $args['field'];\n $field_type = $args['type'] ?? 'text';\n $value = $options[$field_name] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n waa_debug_log(\"Rendering field: $field_name\", [\n 'type' => $field_type,\n 'value' => $value,\n 'class' => $class\n ]);\n \n ?>\n' . esc_html($args['description']) . '
';\n }\n}\n\npublic function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n $class = $args['class'] ?? '';\n \n // Build data attributes\n $data_attrs = '';\n if (!empty($args['data'])) {\n foreach ($args['data'] as $key => $val) {\n $data_attrs .= ' data-' . esc_attr($key) . '=\"' . esc_attr($val) . '\"';\n }\n }\n \n $id = $args['id'] ?? 'waa_options_' . esc_attr($field);\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n}\n\npublic function register_settings(): void {\n // Register the settings group\n register_setting(\n 'waa_options', // Option group\n 'waa_options', // Option name in the database\n ['sanitize_callback' => [$this, 'sanitize_options']]\n );\n\n // Register settings sections\n $this->register_ai_settings(); // AI provider and model settings\n $this->register_api_settings(); // API credentials for all providers\n $this->register_search_settings(); // Search configuration\n $this->register_display_settings(); // Display options\n $this->register_lead_settings(); // Lead generation settings\n\n // Log registered settings for debugging\n waa_debug_log('Settings registered with the following data:', [\n 'option_group' => 'waa_options',\n 'sections' => [\n 'ai_settings' => 'website-ai-assistant',\n 'api_credentials' => 'website-ai-assistant',\n 'search_settings' => 'website-ai-assistant',\n 'display_settings' => 'website-ai-assistant',\n 'lead_settings' => 'website-ai-assistant'\n ]\n ]);\n}\n\nprivate function register_lead_settings(): void {\n add_settings_section(\n 'waa_lead_settings',\n __('Lead Generation Settings', 'website-ai-assistant'),\n [$this, 'render_lead_section'],\n 'website-ai-assistant'\n );\n\n // Enable lead collection\n add_settings_field(\n 'enable_lead_collection',\n __('Enable Lead Collection', 'website-ai-assistant'),\n [$this, 'render_checkbox_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'enable_lead_collection',\n 'description' => __('Show lead collection form in chat widget', 'website-ai-assistant')\n ]\n );\n\n // Lead collection timing\n add_settings_field(\n 'lead_collection_timing',\n __('When to Show Form', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_timing',\n 'options' => [\n 'immediate' => __('Immediately', 'website-ai-assistant'),\n 'after_first' => __('After First Message', 'website-ai-assistant'),\n 'after_two' => __('After Two Messages', 'website-ai-assistant'),\n 'end' => __('After Three Messages', 'website-ai-assistant')\n ],\n 'default' => 'immediate',\n 'description' => __('Choose when to display the lead collection form', 'website-ai-assistant')\n ]\n );\n\n // Form heading\n add_settings_field(\n 'lead_collection_heading',\n __('Form Heading', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_heading',\n 'type' => 'text',\n 'default' => __('Please share your contact info to continue', 'website-ai-assistant'),\n 'description' => __('Heading text displayed at the top of the form', 'website-ai-assistant')\n ]\n );\n\n // Form description\n add_settings_field(\n 'lead_collection_description',\n __('Form Description', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_description',\n 'type' => 'text',\n 'description' => __('Optional description text below the heading', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM List selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_list_id',\n __('FluentCRM List', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_list_id',\n 'options' => $this->get_fluentcrm_lists(),\n 'description' => __('Select the FluentCRM list to add contacts to', 'website-ai-assistant')\n ]\n );\n\n // FluentCRM Tag selector (populated dynamically)\n add_settings_field(\n 'fluentcrm_tag_id',\n __('FluentCRM Tag', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_tag_id',\n 'options' => $this->get_fluentcrm_tags(),\n 'description' => __('Select the FluentCRM tag to apply to contacts', 'website-ai-assistant')\n ]\n );\n\n // Contact Status\n add_settings_field(\n 'fluentcrm_status',\n __('Contact Status', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_status',\n 'options' => [\n 'subscribed' => __('Subscribed', 'website-ai-assistant'),\n 'pending' => __('Pending', 'website-ai-assistant'),\n 'unsubscribed' => __('Unsubscribed', 'website-ai-assistant')\n ],\n 'default' => 'subscribed',\n 'description' => __('Select the default status for new contacts', 'website-ai-assistant')\n ]\n );\n}\n\npublic function render_lead_section(): void {\n echo '' . esc_html__('Configure lead generation settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure your AI settings below:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('API Credentials:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Search Provider Settings:', 'website-ai-assistant') . '
';\n echo '' . esc_html__('Configure where the chat widget should appear.', 'website-ai-assistant') . '
';\n }\n\n\n /* Commented out duplicate method - preserved for reference\n public function render_textarea_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? '';\n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function handle_fetch_models(): void {\n error_log('Starting handle_fetch_models...');\n check_ajax_referer('waa_admin_nonce', 'nonce');\n \n if (!current_user_can('manage_options')) {\n error_log('Unauthorized access attempt in handle_fetch_models');\n wp_send_json_error(['message' => __('Unauthorized access', 'website-ai-assistant')]);\n return;\n }\n \n $provider = sanitize_text_field($_POST['provider'] ?? '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('handle_fetch_models called', [\n 'provider' => $provider,\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'post_data' => $_POST,\n 'request_method' => $_SERVER['REQUEST_METHOD']\n ]);\n \n $models = [];\n \n switch ($provider) {\n case 'gemini':\n $models = \\Website_Ai_Assistant\\Models\\Gemini_Service::AVAILABLE_MODELS;\n update_option('waa_gemini_models', $models);\n waa_debug_log('Using static Gemini models');\n break;\n case 'openai':\n try {\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n waa_debug_log('OpenAI: Processing model fetch request');\n \n waa_debug_log('OpenAI: Processing fetch models request', [\n 'has_api_key' => !empty($api_key),\n 'api_key_length' => strlen($api_key),\n 'api_key_preview' => substr($api_key, 0, 10) . '...'\n ]);\n\n // Check for cached models first\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n \n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n \n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n \n // If no cache hit, determine whether to use cache for fetching\n $options = get_option('waa_options', []);\n $force_refresh = isset($_POST['force_refresh']) && $_POST['force_refresh'] === 'true';\n $use_cache = !$force_refresh && empty($options['openai_disable_cache']);\n\n waa_debug_log('OpenAI: Cache Settings:', [\n 'use_cache' => $use_cache,\n 'force_refresh' => $force_refresh ? 'yes' : 'no',\n 'disable_cache_setting' => !empty($options['openai_disable_cache']) ? 'yes' : 'no'\n ]);\n \n if ($use_cache) {\n $cached_models = get_option('waa_openai_models', []);\n $cached_key_hash = get_option('waa_openai_key_hash', '');\n $current_key_hash = md5($api_key);\n\n waa_debug_log('OpenAI: Cache status', [\n 'has_cached_models' => !empty($cached_models),\n 'models_count' => count($cached_models),\n 'cache_key_match' => $cached_key_hash === $current_key_hash,\n 'current_key_hash' => $current_key_hash\n ]);\n\n // Use cached models if available and key matches\n if ($cached_models && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('OpenAI: Using cached models');\n break;\n }\n }\n \n // If we get here, either cache is disabled or we need fresh models\n waa_debug_log('OpenAI: Fetching fresh models from API');\n $openai_service = new OpenAI_Service($api_key);\n $models = $openai_service->get_available_models();\n \n // Cache the models unless this was a forced refresh\n if (!$force_refresh && !empty($models)) {\n update_option('waa_openai_models', $models);\n update_option('waa_openai_key_hash', md5($api_key));\n waa_debug_log('OpenAI: Cached new models');\n }\n\n // Set default model if none selected\n if (!empty($models)) {\n $options = get_option('waa_options', []);\n if (empty($options['openai_model'])) {\n $first_model = array_key_first($models);\n $options['openai_model'] = $first_model;\n update_option('waa_options', $options);\n waa_debug_log('OpenAI: Set default model:', $first_model);\n }\n }\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n case 'deepseek':\n // Check for cached models first\n $cached_models = get_option('waa_deepseek_models', []);\n $cached_key_hash = get_option('waa_deepseek_key_hash', '');\n $api_key = sanitize_text_field($_POST['api_key'] ?? '');\n $current_key_hash = md5($api_key);\n\n // Use cached models if available and key matches\n if (!empty($cached_models) && $cached_key_hash === $current_key_hash) {\n $models = $cached_models;\n waa_debug_log('Deepseek: Using cached models');\n break;\n }\n\n // If no cache hit, validate API key and fetch models\n if (empty($api_key)) {\n throw new \\Exception(__('API key is required', 'website-ai-assistant'));\n }\n\n try {\n $deepseek_service = new Deepseek_Service($api_key, Deepseek_Service::API_ENDPOINT);\n $models = $deepseek_service->get_available_models();\n \n // Cache the models and API key hash\n update_option('waa_deepseek_models', $models);\n update_option('waa_deepseek_key_hash', $current_key_hash);\n waa_debug_log('Deepseek: Cached new models');\n } catch (\\Exception $e) {\n wp_send_json_error(['message' => $e->getMessage()]);\n return;\n }\n break;\n default:\n wp_send_json_error(['message' => __('Invalid provider', 'website-ai-assistant')]);\n return;\n }\n \n wp_send_json_success(['models' => $models]);\n }\n\n /* Commented out duplicate method - preserved for reference\n public function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? $args['default'] ?? '';\n \n echo '';\n \n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n */\n\n public function render_checkbox_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $checked = isset($options[$field]) && $options[$field] ? 'checked' : '';\n \n echo '';\n if (!empty($args['description'])) {\n echo '' . esc_html($args['description']) . '
';\n }\n }\n\n public function render_display_locations_field(): void {\n $options = get_option('waa_options', []);\n $locations = $options['display_locations'] ?? ['all'];\n ?>\n \n admin_url('admin-ajax.php'),\n 'nonce' => wp_create_nonce('waa_admin_nonce'),\n 'i18n' => [\n 'loadingModels' => __('Loading available models...', 'website-ai-assistant'),\n 'enterApiKey' => __('Please enter API key first', 'website-ai-assistant'),\n 'selectModel' => __('Select a model', 'website-ai-assistant'),\n 'fetchError' => __('Failed to fetch models', 'website-ai-assistant'),\n 'noModelsFound' => __('No available models found', 'website-ai-assistant'),\n 'fetchingModels' => __('Fetching available models...', 'website-ai-assistant')\n ]\n ]);\n }\n\n public function sanitize_options($input): array {\n waa_debug_log('Sanitizing input options:', [\n 'ai_provider' => $input['ai_provider'] ?? 'not set',\n 'search_provider' => $input['search_provider'] ?? 'not set',\n 'fields_present' => array_keys($input)\n ]);\n\n // Get existing options to preserve any values not in current input\n $existing = get_option('waa_options', []);\n $sanitized = $existing;\n\n // Handle search provider changes\n if (isset($input['search_provider'])) {\n $search_provider = sanitize_text_field($input['search_provider']);\n if (in_array($search_provider, ['google', 'algolia'])) {\n $old_provider = $existing['search_provider'] ?? 'google';\n $sanitized['search_provider'] = $search_provider;\n \n waa_debug_log('Search provider updated:', [\n 'from' => $old_provider,\n 'to' => $search_provider\n ]);\n\n // Only clear settings if they're explicitly empty in the input\n if ($search_provider === 'google') {\n // When switching to Google, preserve existing Algolia settings\n $sanitized['google_search_api_key'] = $input['google_search_api_key'] ?? $existing['google_search_api_key'] ?? '';\n $sanitized['search_engine_id'] = $input['search_engine_id'] ?? $existing['search_engine_id'] ?? '';\n } else if ($search_provider === 'algolia') {\n // When switching to Algolia, preserve existing Google settings\n $sanitized['algolia_app_id'] = $input['algolia_app_id'] ?? $existing['algolia_app_id'] ?? '';\n $sanitized['algolia_search_key'] = $input['algolia_search_key'] ?? $existing['algolia_search_key'] ?? '';\n $sanitized['algolia_admin_key'] = $input['algolia_admin_key'] ?? $existing['algolia_admin_key'] ?? '';\n $sanitized['algolia_index'] = $input['algolia_index'] ?? $existing['algolia_index'] ?? '';\n }\n \n waa_debug_log('Settings after provider change:', [\n 'provider' => $search_provider,\n 'has_google_key' => isset($sanitized['google_search_api_key']),\n 'has_algolia_key' => isset($sanitized['algolia_search_key'])\n ]);\n }\n }\n \n $field_types = [\n 'url' => ['deepseek_endpoint'],\n 'key' => [\n 'gemini_api_key', 'openai_api_key', 'deepseek_api_key',\n 'google_search_api_key', 'search_engine_id',\n 'algolia_app_id', 'algolia_search_key', 'algolia_admin_key'\n ],\n 'text' => ['algolia_index']\n ];\n\n // Process each field type\n foreach ($field_types as $type => $fields) {\n foreach ($fields as $field) {\n if (!isset($input[$field])) continue;\n\n switch ($type) {\n case 'url':\n $sanitized[$field] = esc_url_raw(rtrim($input[$field], '/') . '/');\n break;\n case 'key':\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n if ($type === 'text') {\n waa_debug_log(\"Sanitizing text field: $field\", [\n 'input_value' => $input[$field],\n 'sanitized_value' => $sanitized[$field]\n ]);\n }\n break;\n }\n }\n }\n\n // Provider and model fields\n $provider_model_fields = [\n 'ai_provider',\n 'openai_model',\n 'gemini_model',\n 'deepseek_model',\n 'search_provider',\n 'system_message'\n ];\n\n foreach ($provider_model_fields as $field) {\n if (isset($input[$field])) {\n $sanitized[$field] = sanitize_text_field($input[$field]);\n waa_debug_log(\"Sanitized $field:\", $sanitized[$field]);\n } else {\n waa_debug_log(\"Field not found in input: $field\");\n $sanitized[$field] = '';\n }\n }\n\n // Add special handling for provider-specific model fields\n $provider = $sanitized['ai_provider'] ?? '';\n if ($provider) {\n $model_field = $provider . '_model';\n waa_debug_log(\"Checking provider-specific model field: $model_field\", [\n 'value' => $sanitized[$model_field] ?? 'not set',\n 'provider' => $provider\n ]);\n }\n\n // Handle display locations array\n if (isset($input['display_locations']) && is_array($input['display_locations'])) {\n $sanitized['display_locations'] = array_map('sanitize_text_field', $input['display_locations']);\n } else {\n $sanitized['display_locations'] = ['all']; // Default to all pages if not set\n }\n\n // Handle debug option\n $sanitized['enable_debug'] = isset($input['enable_debug']) ? 1 : 0;\n\n // Sanitize lead generation fields\n $lead_fields = [\n 'enable_lead_collection' => 'bool',\n 'lead_collection_timing' => 'text',\n 'lead_collection_heading' => 'text',\n 'lead_collection_description' => 'text',\n 'fluentcrm_list_id' => 'int',\n 'fluentcrm_tag_id' => 'int',\n 'fluentcrm_status' => 'text'\n ];\n\n foreach ($lead_fields as $field => $type) {\n if (isset($input[$field])) {\n switch ($type) {\n case 'bool':\n $sanitized[$field] = (bool) $input[$field];\n break;\n case 'int':\n $sanitized[$field] = absint($input[$field]);\n break;\n case 'text':\n $sanitized[$field] = sanitize_text_field($input[$field]);\n break;\n }\n }\n }\n\n return $sanitized;\n }\n\n}\n"}, "43f4fa331b6e5efcda109c72c11afecee008edfb": {"t": 1785756321.2304368, "n": 27801, "h": "5388789266ff3622", "c": " [$this, 'sanitize_options'],\n 'default' => []\n ]\n );\n\n // API Settings section\n add_settings_section(\n 'waa_api_settings',\n __('API Settings', 'website-ai-assistant'),\n [$this, 'render_api_section'],\n 'website-ai-assistant'\n );\n\n // Search Provider selector\n add_settings_field(\n 'search_provider',\n __('Search Provider', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'field' => 'search_provider',\n 'options' => [\n 'google' => __('Google Custom Search', 'website-ai-assistant'),\n 'algolia' => __('Algolia (WordPress Plugin)', 'website-ai-assistant')\n ],\n 'default' => 'google',\n 'description' => __('Select which search provider to use. Algolia requires the Algolia Search plugin to be installed and configured.', 'website-ai-assistant')\n ]\n );\n\n // Gemini API Key field\n add_settings_field(\n 'gemini_api_key',\n __('Gemini API Key', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'gemini_api_key',\n 'field_name' => 'gemini_api_key',\n 'type' => 'password'\n ]\n );\n\n // Google Search Fields\n add_settings_field(\n 'google_search_api_key',\n __('Google Search API Key', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'google_search_api_key',\n 'field_name' => 'google_search_api_key',\n 'type' => 'password',\n 'class' => 'google-search-field'\n ]\n );\n\n add_settings_field(\n 'search_engine_id',\n __('Search Engine ID', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'search_engine_id',\n 'field_name' => 'search_engine_id',\n 'type' => 'text',\n 'class' => 'google-search-field'\n ]\n );\n\n // Algolia Fields\n add_settings_field(\n 'algolia_app_id',\n __('Algolia Application ID', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'algolia_app_id',\n 'field_name' => 'algolia_app_id',\n 'type' => 'text',\n 'class' => 'algolia-field'\n ]\n );\n\n add_settings_field(\n 'algolia_search_key',\n __('Algolia Search API Key', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'algolia_search_key',\n 'field_name' => 'algolia_search_key',\n 'type' => 'password',\n 'class' => 'algolia-field'\n ]\n );\n\n add_settings_field(\n 'algolia_admin_key',\n __('Algolia Admin API Key', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n 'website-ai-assistant',\n 'waa_api_settings',\n [\n 'label_for' => 'algolia_admin_key',\n 'field_name' => 'algolia_admin_key',\n 'type' => 'password',\n 'class' => 'algolia-field'\n ]\n );\n\n add_settings_field(\n 'system_message',\n __('System Message', 'website-ai-assistant'),\n [$this, 'render_textarea_field'],\n self::SETTINGS_PAGE,\n 'waa_api_settings',\n ['field' => 'system_message']\n );\n\n add_settings_field(\n 'max_history',\n __('Max Conversation History', 'website-ai-assistant'),\n [$this, 'render_number_field'],\n self::SETTINGS_PAGE,\n 'waa_api_settings',\n ['field' => 'max_history']\n );\n\n // Add Website Topics field after system message\n add_settings_field(\n 'website_topics',\n __('Website Topics', 'website-ai-assistant'),\n [$this, 'render_textarea_field'],\n self::SETTINGS_PAGE,\n 'waa_api_settings',\n [\n 'field' => 'website_topics',\n 'description' => __('Enter the main topics or subjects your website covers (e.g., \"hydroponics, indoor gardening, plant care\"). This helps the AI understand which questions it should answer in detail.', 'website-ai-assistant')\n ]\n );\n\n // Add new section for Display Settings\n add_settings_section(\n 'waa_display_settings',\n __('Display Settings', 'website-ai-assistant'),\n [$this, 'render_display_section'],\n self::SETTINGS_PAGE\n );\n\n // Add display location field\n add_settings_field(\n 'display_locations',\n __('Show Chat Widget On', 'website-ai-assistant'),\n [$this, 'render_display_locations_field'],\n self::SETTINGS_PAGE,\n 'waa_display_settings'\n );\n\n // Add new section for Lead Collection Settings\n add_settings_section(\n 'waa_lead_settings',\n __('Lead Collection Settings', 'website-ai-assistant'),\n [$this, 'render_lead_section'],\n self::SETTINGS_PAGE\n );\n\n // Add lead collection fields\n add_settings_field(\n 'enable_lead_collection',\n __('Enable Lead Collection', 'website-ai-assistant'),\n [$this, 'render_checkbox_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n ['field' => 'enable_lead_collection']\n );\n\n add_settings_field(\n 'lead_collection_timing',\n __('Ask for Contact Info', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_timing',\n 'options' => [\n 'immediate' => __('At the start of conversation', 'website-ai-assistant'),\n 'after_first' => __('After first message', 'website-ai-assistant'),\n 'after_two' => __('After two messages', 'website-ai-assistant'),\n 'end' => __('At end of conversation', 'website-ai-assistant')\n ]\n ]\n );\n\n // FluentCRM List field with priority 10\n add_settings_field(\n 'fluentcrm_list_id',\n __('FluentCRM List', 'website-ai-assistant'),\n [$this, 'render_fluentcrm_lists_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_list_id',\n 'priority' => 10\n ]\n );\n\n // FluentCRM Tag field with priority 11\n add_settings_field(\n 'fluentcrm_tag_id',\n __('FluentCRM Tag', 'website-ai-assistant'),\n [$this, 'render_fluentcrm_tags_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_tag_id',\n 'priority' => 11\n ]\n );\n\n // Add after the FluentCRM tag field\n add_settings_field(\n 'fluentcrm_status',\n __('Contact Status', 'website-ai-assistant'),\n [$this, 'render_select_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'fluentcrm_status',\n 'options' => [\n 'subscribed' => __('Subscribed', 'website-ai-assistant'),\n 'pending' => __('Pending', 'website-ai-assistant')\n ],\n 'description' => __('Status for new contacts in FluentCRM', 'website-ai-assistant'),\n 'default' => 'subscribed'\n ]\n );\n\n // Add lead collection message fields\n add_settings_field(\n 'lead_collection_heading',\n __('Lead Collection Heading', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_heading',\n 'description' => __('The heading shown above the contact form', 'website-ai-assistant'),\n 'default' => __('Please share your contact info to continue', 'website-ai-assistant')\n ]\n );\n\n add_settings_field(\n 'lead_collection_description',\n __('Lead Collection Description', 'website-ai-assistant'),\n [$this, 'render_textarea_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'lead_collection_description',\n 'description' => __('Optional description text shown below the heading', 'website-ai-assistant')\n ]\n );\n\n add_settings_field(\n 'privacy_page_url',\n __('Privacy Policy URL', 'website-ai-assistant'),\n [$this, 'render_privacy_page_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'description' => __('Select your privacy policy page or enter a custom URL', 'website-ai-assistant')\n ]\n );\n\n add_settings_field(\n 'privacy_text',\n __('Privacy Notice Text', 'website-ai-assistant'),\n [$this, 'render_text_field'],\n self::SETTINGS_PAGE,\n 'waa_lead_settings',\n [\n 'field' => 'privacy_text',\n 'description' => __('Text shown with privacy policy link. Use {privacy_policy} for the link.', 'website-ai-assistant'),\n 'default' => __('By continuing, you agree to our {privacy_policy}.', 'website-ai-assistant')\n ]\n );\n\n // Add in register_settings() method after other settings\n add_settings_field(\n 'enable_debug',\n __('Enable Debug Logging', 'website-ai-assistant'),\n [$this, 'render_checkbox_field'],\n self::SETTINGS_PAGE,\n 'waa_api_settings',\n [\n 'field' => 'enable_debug',\n 'description' => __('Log debug information to wp-content/debug.log', 'website-ai-assistant')\n ]\n );\n }\n\n public function render_settings_page(): void {\n if (!current_user_can('manage_options')) {\n return;\n }\n require_once WAA_PLUGIN_DIR . 'admin/views/settings-page.php';\n }\n\n public function enqueue_admin_assets($hook): void {\n if (\"settings_page_\" . self::SETTINGS_PAGE !== $hook) {\n return;\n }\n\n wp_enqueue_style(\n 'waa-admin-settings',\n WAA_PLUGIN_URL . 'admin/css/admin-settings.css',\n [],\n WAA_VERSION\n );\n\n wp_enqueue_script(\n 'waa-admin-settings',\n WAA_PLUGIN_URL . 'admin/js/admin-settings.js',\n ['jquery'],\n WAA_VERSION,\n true\n );\n\n // Add AJAX data for the test buttons\n wp_localize_script('waa-admin-settings', 'waaAdmin', [\n 'ajaxUrl' => admin_url('admin-ajax.php'),\n 'nonce' => wp_create_nonce('waa_admin_nonce'),\n 'testGemini' => __('Testing Gemini API...', 'website-ai-assistant'),\n 'testSearch' => __('Testing Search API...', 'website-ai-assistant'),\n 'enterQuery' => __('Please enter a search query to test.', 'website-ai-assistant'),\n 'ajaxError' => __('Network error occurred. Please try again.', 'website-ai-assistant'),\n 'missingSearchCreds' => __('Please enter both Google Search API key and Search Engine ID.', 'website-ai-assistant'),\n ]);\n }\n\n /**\n * Renders a text field for the settings page\n *\n * @param array $args Field arguments\n */\n public function render_text_field($args): void {\n $options = get_option('waa_options', []);\n $field_name = $args['field'] ?? $args['field_name'] ?? '';\n $field_type = $args['type'] ?? 'text';\n $value = $options[$field_name] ?? $args['default'] ?? '';\n ?>\n \"\n id=\"\"\n name=\"waa_options[]\"\n value=\"\"\n class=\"regular-text\"\n />\n ' . esc_textarea($value) . '';\n if ($field === 'system_message') {\n echo '' . esc_html__('This message sets the AI\\'s behavior and tone. Leave blank for default.', 'website-ai-assistant') . '
';\n }\n }\n\n /**\n * Renders a number field for the settings page\n *\n * @param array $args Field arguments\n */\n public function render_number_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? 10;\n echo '';\n if ($field === 'max_history') {\n echo '' . esc_html__('Maximum number of messages to keep in conversation history. Default is 10.', 'website-ai-assistant') . '
';\n }\n }\n\n /**\n * Renders the API settings section description\n */\n public function render_api_section(): void {\n echo '' . esc_html__('Configure your AI Assistant settings below. You\\'ll need a Gemini API key to use this plugin.', 'website-ai-assistant') . '
';\n }\n\n /**\n * Validates and sanitizes the options before saving\n *\n * @param array $input The raw input array\n * @return array The sanitized output array\n */\n public function sanitize_options($input) {\n // Debug logging\n error_log('Sanitizing options - Input: ' . print_r($input, true));\n \n $sanitized = [];\n \n // Handle API keys\n // Process API keys\n $api_fields = ['gemini_api_key', 'google_search_api_key', 'search_engine_id'];\n foreach ($api_fields as $field) {\n $sanitized[$field] = isset($input[$field]) ? sanitize_text_field($input[$field]) : '';\n }\n\n // Process lead collection settings\n $lead_fields = [\n 'enable_lead_collection' => 'bool',\n 'lead_collection_timing' => 'text',\n 'fluentcrm_list_id' => 'int',\n 'fluentcrm_tag_id' => 'int',\n 'fluentcrm_status' => 'text',\n 'lead_collection_heading' => 'text',\n 'lead_collection_description' => 'textarea',\n 'privacy_page_url' => 'url',\n 'privacy_text' => 'text'\n ];\n\n foreach ($lead_fields as $field => $type) {\n switch ($type) {\n case 'bool':\n $sanitized[$field] = isset($input[$field]) ? 1 : 0;\n break;\n case 'int':\n $sanitized[$field] = isset($input[$field]) ? absint($input[$field]) : 0;\n break;\n case 'url':\n $sanitized[$field] = isset($input[$field]) ? esc_url_raw($input[$field]) : '';\n break;\n case 'textarea':\n $sanitized[$field] = isset($input[$field]) ? sanitize_textarea_field($input[$field]) : '';\n break;\n default:\n $sanitized[$field] = isset($input[$field]) ? sanitize_text_field($input[$field]) : '';\n }\n }\n\n // Merge with existing options while preserving unrelated settings\n $existing = get_option('waa_options', []);\n // Merge with new sanitized values taking priority over existing ones\n $sanitized = array_merge($existing, $sanitized);\n $sanitized = array_merge($sanitized, $input);\n \n // Debug logging\n error_log('Sanitized options: ' . print_r($sanitized, true));\n \n return $sanitized;\n }\n\n public function render_display_section(): void {\n echo '' . esc_html__('Configure where the chat widget should appear on your website.', 'website-ai-assistant') . '
';\n }\n\n public function render_display_locations_field(): void {\n $options = get_option('waa_options', []);\n $locations = $options['display_locations'] ?? ['all'];\n ?>\n \n\n \n
\n ' . esc_html__('Configure settings for collecting user contact information.', 'website-ai-assistant') . '';\n }\n\n public function render_checkbox_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $checked = (isset($options[$field]) && $options[$field] == 1) ? 'checked' : '';\n \n echo '';\n }\n\n public function render_select_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $value = $options[$field] ?? '';\n \n echo '';\n }\n\n /**\n * Renders the FluentCRM lists dropdown\n */\n public function render_fluentcrm_lists_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $selected_list = $options[$field] ?? '';\n \n if (!function_exists('FluentCrmApi')) {\n echo '' . \n esc_html__('FluentCRM is not installed or activated.', 'website-ai-assistant') . \n '
';\n return;\n }\n\n try {\n $lists = FluentCrmApi('lists')->all();\n \n if (empty($lists)) {\n echo '' . \n esc_html__('No lists found in FluentCRM. Please create a list first.', 'website-ai-assistant') . \n '
';\n return;\n }\n\n echo '';\n echo '' . \n esc_html__('Select the FluentCRM list where leads will be added.', 'website-ai-assistant') . \n '
';\n\n } catch (\\Exception $e) {\n echo '' . \n esc_html__('Error loading FluentCRM lists. Please check if FluentCRM is configured correctly.', 'website-ai-assistant') . \n '
';\n }\n }\n\n /**\n * Renders the FluentCRM tags dropdown\n */\n public function render_fluentcrm_tags_field(array $args): void {\n $options = get_option('waa_options');\n $field = $args['field'];\n $selected_tag = $options[$field] ?? '';\n \n if (!function_exists('FluentCrmApi')) {\n echo '' . \n esc_html__('FluentCRM is not installed or activated.', 'website-ai-assistant') . \n '
';\n return;\n }\n\n try {\n $tags = FluentCrmApi('tags')->all();\n \n if (empty($tags)) {\n echo '' . \n esc_html__('No tags found in FluentCRM. Please create a tag first.', 'website-ai-assistant') . \n '
';\n return;\n }\n\n echo '';\n echo '' . \n esc_html__('Select the FluentCRM tag to apply to new contacts.', 'website-ai-assistant') . \n '
';\n\n } catch (\\Exception $e) {\n echo '' . \n esc_html__('Error loading FluentCRM tags.', 'website-ai-assistant') . \n '
';\n }\n }\n\n /**\n * Renders the privacy page selector\n */\n public function render_privacy_page_field(array $args): void {\n $options = get_option('waa_options');\n $current_url = $options['privacy_page_url'] ?? '';\n \n // Get list of pages\n $pages = get_pages(['sort_column' => 'post_title']);\n \n echo '';\n \n // Custom URL input\n echo '' . esc_html($args['description']) . '
';\n }\n }\n\n /**\n * Updates the system message to include website topics\n */\n private function get_system_message(): string {\n $options = get_option('waa_options', []);\n $topics = $options['website_topics'] ?? '';\n \n $system_message = __(\n 'You are an AI assistant specifically focused on helping with questions about this website. ',\n 'website-ai-assistant'\n );\n \n if (!empty($topics)) {\n $system_message .= sprintf(\n __('This website focuses on the following topics: %s. ', 'website-ai-assistant'),\n $topics\n );\n }\n \n $system_message .= __(\n 'For questions directly related to these topics and website content, provide detailed answers with relevant links. ' .\n 'For unrelated questions, politely explain that you are specifically here to help with website-related questions and provide only a brief, general response without links.',\n 'website-ai-assistant'\n );\n \n return $system_message;\n }\n} "}}}}