PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.5
MxChat – AI Chatbot & Content Generation for WordPress v2.5.5
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | includes/class-mxchat-admin.php +5076 -3144 2.0.32.5.5 View file →
@@ -6,13 +6,15 @@
6 6 class MxChat_Admin {
7 7 private $options;
8 8 private $chat_count;
9 9 private $is_activated;
10 + private $knowledge_manager;
10 11
11 - public function __construct() {
12 + public function __construct($knowledge_manager = null) {
12 13 $this->options = get_option('mxchat_options');
13 14 $this->chat_count = get_option('mxchat_chat_count', 0);
14 15 $this->is_activated = $this->is_license_active();
16 + $this->knowledge_manager = $knowledge_manager;
15 17
16 18 // Initialize default options if they are not set
17 19 if (!$this->options) {
18 20 $this->initialize_default_options();
@@ -23,49 +25,42 @@
23 25 add_action('admin_init', array($this, 'mxchat_page_init'));
24 26 add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
25 27 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 28 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
27 - add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
28 29 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 30 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 31 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 - add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
32 32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 34 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 - add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
36 35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
37 - add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 36 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39 37 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
40 38 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
41 - add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
42 - add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
43 - add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
44 - add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 - add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 - add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 - add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 - add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
49 - add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
50 - if (isset($this->options['enable_woocommerce_integration']) &&
51 - ($this->options['enable_woocommerce_integration'] === '1' ||
52 - $this->options['enable_woocommerce_integration'] === 'on')) {
39 + add_action('admin_post_mxchat_edit_intent', array($this, 'mxchat_handle_edit_intent'));
40 + add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
41 + add_action('admin_init', array($this, 'mxchat_transcripts_page_init'));
42 + add_action('wp_ajax_dismiss_live_agent_notice', array($this, 'dismiss_live_agent_notice'));
53 43
54 - //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
55 - add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
56 - add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
57 - add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
58 - }
44 + add_action('admin_init', array($this, 'register_pinecone_settings'));
45 +
46 + add_action('admin_notices', array($this, 'display_admin_notices'));
47 +
48 + add_action('wp_ajax_mxchat_test_streaming_actual', [$this, 'mxchat_handle_test_streaming_actual']);
49 + add_action('wp_ajax_mxchat_test_streaming', [$this, 'mxchat_handle_test_streaming']); // Keep existing as fallback
50 +
51 + add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
52 + add_action('wp_ajax_mxchat_fetch_openrouter_models', array($this, 'fetch_openrouter_models'));
59 53 }
60 54
61 - // Method to check if the license is active
62 55 private function is_license_active() {
63 - $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
64 - return $license_status === esc_html__('active', 'mxchat');
65 - }
56 + // Get the raw value without translation
57 + $license_status = get_option('mxchat_license_status', 'inactive');
66 58
67 - // Initialize default options
59 + // Check against multiple possible values, bypassing translation issues
60 + return ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
61 +}
62 +
68 63 private function initialize_default_options() {
69 64 $default_options = array(
70 65 'api_key' => '',
71 66 'xai_api_key' => '',
@@ -70,25 +65,46 @@
70 65 'api_key' => '',
71 66 'xai_api_key' => '',
72 67 'claude_api_key' => '',
73 68 'deepseek_api_key' => '',
74 - 'system_prompt_instructions' => esc_html__('[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind:
75 - - Your name is [Chatbot Name].
76 - - Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject.
77 - - Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs.
78 - - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
79 - - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'),
69 + 'voyage_api_key' => '',
70 + 'gemini_api_key' => '',
71 + 'enable_streaming_toggle' => 'on',
72 + 'embedding_model' => 'text-embedding-ada-002',
73 + 'system_prompt_instructions' => 'You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
74 +
75 + # Response Style - CRITICALLY IMPORTANT
76 + - MAXIMUM LENGTH: 1-3 short sentences per response
77 + - Ultra-concise: Get straight to the answer with no filler
78 + - No introductions like "Sure!" or "I\'d be happy to help"
79 + - No phrases like "based on my knowledge" or "according to information"
80 + - No explanatory text before giving the answer
81 + - No summaries or repetition
82 + - Hyperlink all URLs
83 + - Respond in user\'s language
84 + - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
85 +
86 + # Knowledge Base Requirements - PREVENT HALLUCINATIONS
87 + - ONLY answer questions using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
88 + - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
89 + - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
90 + - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
91 + - Better to admit insufficient information than provide inaccurate answers',
80 92 'model' => esc_html__('gpt-4o', 'mxchat'),
81 93 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
82 94 'role_rate_limits' => array(),
83 95 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
84 96 'enable_email_block' => '',
85 - 'email_blocker_header_content' => esc_html__("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
97 + 'email_blocker_header_content' => __("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
86 98 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
99 + 'enable_name_field' => 'off', // NEW
100 + 'name_field_placeholder' => esc_html__('Enter your name', 'mxchat'), // NEW
87 101 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
88 - 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
102 + 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
103 + 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
89 104 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
90 105 'append_to_body' => esc_html__('off', 'mxchat'),
106 + 'contextual_awareness_toggle' => 'off',
91 107 'close_button_color' => esc_html__('#fff', 'mxchat'),
92 108 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
93 109 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
94 110 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
@@ -105,17 +121,19 @@
105 121
106 122 // New fields for Loops Integration
107 123 'loops_api_key' => '',
108 124 'loops_mailing_list' => '',
109 - 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
110 - 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
125 + 'triggered_phrase_response' => __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
126 + 'email_capture_response' => __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
111 127 'popular_question_1' => '',
112 128 'popular_question_2' => '',
113 129 'popular_question_3' => '',
114 - 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
115 - 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
116 - 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
130 + 'pdf_intent_trigger_text' => __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
131 + 'pdf_intent_success_text' => __("I've processed the PDF. What questions do you have about it?", 'mxchat'),
132 + 'pdf_intent_error_text' => __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
117 133 'pdf_max_pages' => 69,
134 + 'show_pdf_upload_button' => 'on',
135 + 'show_word_upload_button' => 'on',
118 136
119 137 // Live Agent Integration
120 138 'live_agent_webhook_url' => '',
121 139 'live_agent_secret_key' => '',
@@ -149,10 +167,8 @@
149 167 // Update the $this->options property
150 168 $this->options = $merged_options;
151 169 }
152 170
153 -
154 -
155 171 public function mxchat_add_plugin_page() {
156 172 // Main menu page
157 173 add_menu_page(
158 174 esc_html__('MxChat Settings', 'mxchat'),
@@ -173,26 +189,24 @@
173 189 'mxchat-prompts',
174 190 array($this, 'mxchat_create_prompts_page')
175 191 );
176 192
177 - // Submenu page for Chat Transcripts
178 193 add_submenu_page(
179 - 'mxchat-max', // Corrected parent slug to match the main menu
194 + 'mxchat-max',
180 195 esc_html__('Chat Transcripts', 'mxchat'),
181 196 esc_html__('Transcripts', 'mxchat'),
182 197 'manage_options',
183 198 'mxchat-transcripts',
184 - array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
199 + array($this, 'mxchat_create_transcripts_page')
185 200 );
186 201
187 - // Submenu page for Intents
188 202 add_submenu_page(
189 - 'mxchat-max', // Parent slug
190 - esc_html__('MxChat Intents', 'mxchat'), // Page title
191 - esc_html__('Intents', 'mxchat'), // Menu title
192 - 'manage_options', // Capability
193 - 'mxchat-intents', // Menu slug
194 - array($this, 'mxchat_intents_page_html') // Callback function
203 + 'mxchat-max',
204 + esc_html__('MxChat Actions', 'mxchat'),
205 + esc_html__('Actions', 'mxchat'),
206 + 'manage_options',
207 + 'mxchat-actions',
208 + array($this, 'mxchat_actions_page_html')
195 209 );
196 210
197 211 add_submenu_page(
198 212 'mxchat-max',
@@ -219,156 +233,299 @@
219 233 $addons_page = new MxChat_Addons();
220 234 $addons_page->render_page();
221 235 }
222 236
223 -public function mxchat_save_setting_callback() {
224 - check_ajax_referer('mxchat_save_setting_nonce');
225 - if (!current_user_can('manage_options')) {
226 - wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
237 +/**
238 + * Test actual streaming functionality in the WordPress environment
239 + */
240 +public function mxchat_handle_test_streaming_actual() {
241 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
242 +
243 + // Check if headers have already been sent
244 + if (headers_sent()) {
245 + wp_send_json_error(['message' => 'Headers already sent - streaming not possible']);
246 + return;
227 247 }
228 - $name = isset($_POST['name']) ? $_POST['name'] : '';
229 - // Strip slashes from the value before saving
230 - $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
231 - if (empty($name)) {
232 - wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
248 +
249 + // Check for required functions
250 + if (!function_exists('curl_init')) {
251 + wp_send_json_error(['message' => 'cURL not available - streaming requires cURL']);
252 + return;
233 253 }
234 - // Load the full options array
254 +
255 + // Get user's selected model and API key
235 256 $options = get_option('mxchat_options', []);
236 - // Handle special cases
237 - switch ($name) {
238 - case 'additional_popular_questions':
239 - $questions = json_decode($value, true); // No need for stripslashes here
240 - if (is_array($questions)) {
241 - $options[$name] = $questions;
242 - // Also update old option for backwards compatibility
243 - update_option('additional_popular_questions', $questions);
244 - }
257 + $selected_model = $options['model'] ?? 'gpt-4o';
258 +
259 + // Get the provider from the model
260 + $model_parts = explode('-', $selected_model);
261 + $provider = strtolower($model_parts[0]);
262 +
263 + // Get the appropriate API key
264 + $api_key = '';
265 + switch ($provider) {
266 + case 'gpt':
267 + case 'o1':
268 + $api_key = $options['api_key'] ?? '';
245 269 break;
246 - case 'user_message_bg_color':
247 - case 'user_message_font_color':
248 - case 'bot_message_bg_color':
249 - case 'bot_message_font_color':
250 - case 'top_bar_bg_color':
251 - case 'send_button_font_color':
252 - case 'chatbot_background_color':
253 - case 'icon_color':
254 - case 'chat_input_font_color':
255 - case 'live_agent_message_bg_color':
256 - case 'live_agent_message_font_color':
257 - case 'mode_indicator_bg_color':
258 - case 'mode_indicator_font_color':
259 - case 'toolbar_icon_color':
260 - // Store color values directly
261 - $options[$name] = $value;
270 + case 'claude':
271 + $api_key = $options['claude_api_key'] ?? '';
262 272 break;
263 - case 'live_agent_status':
264 - // Set the new value
265 - $options[$name] = ($value === 'on') ? 'on' : 'off';
273 + case 'grok':
274 + $api_key = $options['xai_api_key'] ?? '';
266 275 break;
267 - case 'enable_woocommerce_integration':
268 - // Handle values that used to be 1/0
269 - $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
276 + case 'deepseek':
277 + $api_key = $options['deepseek_api_key'] ?? '';
270 278 break;
279 + case 'gemini':
280 + $api_key = $options['gemini_api_key'] ?? '';
281 + break;
271 282 default:
272 - // Handle role rate limits
273 - if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
274 - // Extract role ID from the name
275 - preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
276 - if (isset($matches[1])) {
277 - $role_id = $matches[1];
278 - // Initialize role_rate_limits if it doesn't exist
279 - if (!isset($options['role_rate_limits'])) {
280 - $options['role_rate_limits'] = [];
281 - }
282 - // Update the specific role's rate limit
283 - $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
284 - break;
285 - }
286 - }
287 - // Handle toggles
288 - if (strpos($name, 'toggle') !== false || in_array($name, [
289 - 'chat_persistence_toggle',
290 - 'privacy_toggle',
291 - 'complianz_toggle',
292 - 'chat_toolbar_toggle' // Removed live_agent_status from here
293 - ])) {
294 - $options[$name] = ($value === 'on') ? 'on' : 'off';
295 - } else {
296 - // Store all other values directly
297 - $options[$name] = $value;
298 - }
283 + // Default to OpenAI for unknown models
284 + $api_key = $options['api_key'] ?? '';
285 + $provider = 'gpt';
299 286 break;
300 287 }
301 - // Save all updates
302 - $updated = update_option('mxchat_options', $options);
303 - // Handle backwards compatibility for certain fields
304 - $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
305 - 'brave_country', 'brave_language', 'similarity_threshold'];
306 - if (in_array($name, $legacy_fields)) {
307 - // Also update the individual option for backwards compatibility
308 - update_option($name, $value);
288 +
289 + if (empty($api_key)) {
290 + wp_send_json_error(['message' => "API key not configured for {$provider} provider"]);
291 + return;
309 292 }
310 - if ($updated) {
311 - wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
312 - } else {
313 - wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
293 +
294 + // Test streaming with the selected model and provider
295 + try {
296 + $this->perform_streaming_test($provider, $selected_model, $api_key);
297 + } catch (Exception $e) {
298 + wp_send_json_error(['message' => 'Streaming test exception: ' . $e->getMessage()]);
314 299 }
315 300 }
316 301
317 302 /**
318 - * Handles AJAX auto-save for prompts and auto-sync settings.
303 + * Perform the actual streaming test
319 304 */
320 -public function mxchat_save_prompts_setting_callback() {
321 - // Check the correct nonce for this action.
322 - check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
305 +private function perform_streaming_test($provider, $model, $api_key) {
306 + // Set streaming headers
307 + header('Content-Type: text/event-stream');
308 + header('Cache-Control: no-cache');
309 + header('X-Accel-Buffering: no'); // Disable nginx buffering
323 310
324 - if ( ! current_user_can( 'manage_options' ) ) {
325 - wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
311 + // Prepare test message
312 + $test_message = "Please respond with exactly: 'Streaming test successful!' - send this as a short response for testing.";
313 +
314 + // Configure API request based on provider
315 + $url = '';
316 + $headers = [];
317 + $body = [];
318 +
319 + switch ($provider) {
320 + case 'gpt':
321 + case 'o1':
322 + $url = 'https://api.openai.com/v1/chat/completions';
323 + $headers = [
324 + 'Content-Type: application/json',
325 + 'Authorization: Bearer ' . $api_key
326 + ];
327 + $body = [
328 + 'model' => $model,
329 + 'messages' => [['role' => 'user', 'content' => $test_message]],
330 + 'max_tokens' => 50,
331 + 'temperature' => 0.3,
332 + 'stream' => true
333 + ];
334 + break;
335 +
336 + case 'claude':
337 + $url = 'https://api.anthropic.com/v1/messages';
338 + $headers = [
339 + 'Content-Type: application/json',
340 + 'x-api-key: ' . $api_key,
341 + 'anthropic-version: 2023-06-01'
342 + ];
343 + $body = [
344 + 'model' => $model,
345 + 'messages' => [['role' => 'user', 'content' => $test_message]],
346 + 'max_tokens' => 50,
347 + 'temperature' => 0.3,
348 + 'stream' => true
349 + ];
350 + break;
351 +
352 + case 'grok':
353 + $url = 'https://api.x.ai/v1/chat/completions';
354 + $headers = [
355 + 'Content-Type: application/json',
356 + 'Authorization: Bearer ' . $api_key
357 + ];
358 + $body = [
359 + 'model' => $model,
360 + 'messages' => [['role' => 'user', 'content' => $test_message]],
361 + 'max_tokens' => 50,
362 + 'temperature' => 0.3,
363 + 'stream' => true
364 + ];
365 + break;
366 +
367 + case 'deepseek':
368 + $url = 'https://api.deepseek.com/v1/chat/completions';
369 + $headers = [
370 + 'Content-Type: application/json',
371 + 'Authorization: Bearer ' . $api_key
372 + ];
373 + $body = [
374 + 'model' => $model,
375 + 'messages' => [['role' => 'user', 'content' => $test_message]],
376 + 'max_tokens' => 50,
377 + 'temperature' => 0.3,
378 + 'stream' => true
379 + ];
380 + break;
381 +
382 + default:
383 + echo "data: " . json_encode(['error' => 'Unsupported provider for streaming test: ' . $provider]) . "\n\n";
384 + flush();
385 + return;
326 386 }
327 387
328 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
329 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
388 + // Initialize cURL
389 + $ch = curl_init();
390 + curl_setopt($ch, CURLOPT_URL, $url);
391 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
392 + curl_setopt($ch, CURLOPT_POST, true);
393 + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
394 + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
395 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
396 + curl_setopt($ch, CURLOPT_TIMEOUT, 30);
397 + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use ($provider) {
398 + return $this->process_streaming_test_data($data, $provider);
399 + });
330 400
331 - if ( empty( $name ) ) {
332 - wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
401 + // Send initial test message
402 + echo "data: " . json_encode(['content' => '[Starting streaming test...]']) . "\n\n";
403 + flush();
404 +
405 + $result = curl_exec($ch);
406 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
407 + $curl_error = curl_error($ch);
408 + curl_close($ch);
409 +
410 + if ($curl_error) {
411 + echo "data: " . json_encode(['error' => 'cURL Error: ' . $curl_error]) . "\n\n";
412 + flush();
413 + return;
333 414 }
334 415
335 - // Handle standalone auto-sync settings.
336 - if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
337 - // Convert checkbox value to "1" if checked, "0" if not.
338 - $value = ( $value === 'on' ) ? '1' : '0';
339 - update_option( $name, $value );
340 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
416 + if ($http_code !== 200) {
417 + echo "data: " . json_encode(['error' => 'API returned HTTP ' . $http_code]) . "\n\n";
418 + flush();
419 + return;
341 420 }
342 421
343 - // Handle fields stored in the 'mxchat_prompts_options' array.
344 -// Handle fields stored in the 'mxchat_prompts_options' array.
345 -if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
346 - // Extract the key name using regex.
347 - if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
348 - $key = sanitize_text_field( $matches[1] );
349 - $options = get_option( 'mxchat_prompts_options', [] );
422 + // Send completion signal
423 + echo "data: [DONE]\n\n";
424 + flush();
425 +}
350 426
351 - // Remove Pinecone-specific settings from the main plugin handling.
352 - if ( 'mxchat_use_pinecone' === $key ) {
353 - // Optionally, you could simply ignore this field or set a default value.
354 - // For example, ensure it's always 0 or remove it entirely:
355 - // $options[ $key ] = '0';
356 - // Or, if you prefer, just return an error so that the main plugin doesn't handle it:
357 - wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
427 +/**
428 + * Process streaming data for the test
429 + */
430 +private function process_streaming_test_data($data, $provider) {
431 + static $chunk_count = 0;
432 +
433 + $lines = explode("\n", $data);
434 +
435 + foreach ($lines as $line) {
436 + if (trim($line) === '') {
437 + continue;
438 + }
439 +
440 + // Handle different provider formats
441 + if ($provider === 'claude') {
442 + // Claude uses event: and data: format
443 + if (strpos($line, 'data: ') === 0) {
444 + $json_str = substr($line, 6);
445 + $json = json_decode($json_str, true);
446 +
447 + if (isset($json['type']) && $json['type'] === 'content_block_delta') {
448 + if (isset($json['delta']['text'])) {
449 + $chunk_count++;
450 + echo "data: " . json_encode([
451 + 'content' => $json['delta']['text'],
452 + 'test_chunk' => $chunk_count
453 + ]) . "\n\n";
454 + flush();
455 + }
456 + }
457 + }
358 458 } else {
359 - // For all other fields, use the existing logic.
360 - $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
459 + // OpenAI, X.AI, DeepSeek format
460 + if (strpos($line, 'data: ') === 0) {
461 + $json_str = substr($line, 6);
462 +
463 + if ($json_str === '[DONE]') {
464 + // Don't echo [DONE] here, let the main function handle it
465 + continue;
466 + }
467 +
468 + $json = json_decode($json_str, true);
469 + if (isset($json['choices'][0]['delta']['content'])) {
470 + $chunk_count++;
471 + echo "data: " . json_encode([
472 + 'content' => $json['choices'][0]['delta']['content'],
473 + 'test_chunk' => $chunk_count
474 + ]) . "\n\n";
475 + flush();
476 + }
477 + }
361 478 }
362 - update_option( 'mxchat_prompts_options', $options );
363 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
364 479 }
480 +
481 + return strlen($data);
365 482 }
366 483
484 +/**
485 + * Updated version of your existing test method (keep this as a fallback)
486 + */
487 +public function mxchat_handle_test_streaming() {
488 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
367 489
368 - wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
490 + // Use the actual streaming test instead
491 + $this->mxchat_handle_test_streaming_actual();
369 492 }
370 493
494 +
495 +public function register_pinecone_settings() {
496 + register_setting(
497 + 'mxchat_pinecone_addon_options',
498 + 'mxchat_pinecone_addon_options',
499 + array(
500 + 'type' => 'array',
501 + 'sanitize_callback' => array($this, 'sanitize_pinecone_settings'),
502 + 'default' => array(
503 + 'mxchat_use_pinecone' => '0',
504 + 'mxchat_pinecone_api_key' => '',
505 + 'mxchat_pinecone_host' => '',
506 + 'mxchat_pinecone_index' => '',
507 + 'mxchat_pinecone_environment' => ''
508 + )
509 + )
510 + );
511 +}
512 +
513 +public function sanitize_pinecone_settings($input) {
514 + $sanitized = array();
515 +
516 + $sanitized['mxchat_use_pinecone'] = isset($input['mxchat_use_pinecone']) ? '1' : '0';
517 + $sanitized['mxchat_pinecone_api_key'] = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
518 + $sanitized['mxchat_pinecone_host'] = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
519 + $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
520 + $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
521 +
522 + // Remove https:// from host if present
523 + $sanitized['mxchat_pinecone_host'] = str_replace(['https://', 'http://'], '', $sanitized['mxchat_pinecone_host']);
524 +
525 + return $sanitized;
526 +}
527 +
371 528 public function mxchat_display_admin_notice() {
372 529 // Success notice
373 530 if ($message = get_transient('mxchat_admin_notice_success')) {
374 531 ?>
@@ -391,2041 +548,2437 @@
391 548 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
392 549 }
393 550 }
394 551
552 +public function show_live_agent_disabled_banner() {
553 + $show_disabled_notice = get_option('mxchat_show_live_agent_disabled_notice', false);
395 554
396 -
397 -
398 -
555 + if ($show_disabled_notice) {
556 + ?>
557 + <div class="mxchat-live-agent-disabled-notice" id="mxchat-disabled-notice">
558 + <div class="mxchat-pro-notification">
559 + <button type="button" class="mxchat-dismiss-btn" onclick="dismissLiveAgentNotice()" aria-label="Dismiss notification">
560 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
561 + <line x1="18" y1="6" x2="6" y2="18"></line>
562 + <line x1="6" y1="6" x2="18" y2="18"></line>
563 + </svg>
564 + </button>
565 + <div class="mxchat-live-agent-content">
566 + <h3>🔧 Live Agent Integration Updated!</h3>
567 + <p>We've temporarily disabled your Live Agent integration due to recent enhancements that have made it much better! You can easily turn it back on by going to <strong>Toolbar & Components → Live Agent Settings</strong> and reviewing the new configuration options.</p>
568 + </div>
569 + </div>
570 + </div>
571 + <?php
572 + }
573 +}
399 574 public function mxchat_create_admin_page() {
575 + $this->add_live_agent_nonce();
400 576
401 577 ?>
402 - <div class="wrap mxchat-admin">
403 - <?php if (!$this->is_activated): ?>
404 - <div class="mxchat-pro-banner">
405 - <p>
406 - <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
407 - <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
408 - </p>
578 + <div class="wrap mxchat-wrapper">
579 + <!-- Hero Section -->
580 + <div class="mxchat-hero">
581 + <h1 class="mxchat-main-title">
582 + <span class="mxchat-gradient-text">MxChat</span> Settings
583 + </h1>
584 + <p class="mxchat-hero-subtitle">
585 + <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
586 + </p>
409 587 </div>
410 - <?php endif; ?>
411 588
412 - <div class="mxchat-agents-banner">
413 - <p>
414 - <strong><?php echo esc_html__('The MxChat Add-Ons Platform', 'mxchat'); ?></strong><?php echo esc_html__(' is here! Pro users get unlimited access to our growing collection of powerful add-ons. ', 'mxchat'); ?><a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>"><?php echo esc_html__('Explore all add-ons', 'mxchat'); ?></a><?php echo esc_html__(' including Forms Builder, Theme Customizer, Content Moderation, and more – all included with your lifetime Pro license!', 'mxchat'); ?>
415 - </p>
416 - </div>
589 + <div class="mxchat-content">
590 +
591 + <?php if (!$this->is_activated): ?>
592 + <div class="mxchat-pro-card">
593 + <div class="mxchat-pro-notification">
594 + <div class="mxchat-pro-content">
595 + <h3>🚀 Limited Lifetime Offer: Save 15% on MxChat Pro, Agency, or Agency Plus!</h3>
596 + <p>Unlock <strong>unlimited access</strong> to our growing collection of powerful add-ons including Admin AI Assistant (ChatGPT-like experience), Forms Builder, AI Theme Generator, WooCommerce, multi-bot, and more – all included with your <strong>lifetime license!</strong></p> </div>
597 + <div class="mxchat-pro-cta">
598 + <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade Today', 'mxchat'); ?></a>
599 + <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
600 + </div>
601 + </div>
602 + </div>
603 + <?php endif; ?>
417 604
418 - <h2 class="mxchat-nav-tab-wrapper">
419 - <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
420 - <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
421 - <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
422 - </h2>
605 + <?php
606 + $this->show_live_agent_disabled_banner();
607 + ?>
423 608
424 - <div id="chatbot" class="mxchat-tab-content active">
425 - <div class="mxchat-autosave-section">
426 - <?php do_settings_sections('mxchat-chatbot'); ?>
609 + <!-- Tabs Navigation -->
610 + <div class="mxchat-tabs">
611 + <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
612 + <button class="mxchat-tab-button" data-tab="api-keys"><?php echo esc_html__('API Keys', 'mxchat'); ?></button>
613 + <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
614 + <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
427 615 </div>
428 - </div>
429 616
430 - <div id="embed" class="mxchat-tab-content">
431 - <div class="mxchat-autosave-section">
432 - <div class="mxchat-settings-section">
433 - <h2><?php echo esc_html__('WooCommerce Settings', 'mxchat'); ?></h2>
434 - <table class="form-table">
435 - <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
436 - </table>
617 + <!-- Tab Contents -->
618 + <div id="chatbot" class="mxchat-tab-content active">
619 + <div class="mxchat-card">
620 + <div class="mxchat-autosave-section">
621 + <?php do_settings_sections('mxchat-chatbot'); ?>
622 + </div>
437 623 </div>
438 - <div class="section-divider"></div>
624 + </div>
439 625
440 - <div class="mxchat-settings-section">
441 - <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
442 - <table class="form-table">
443 - <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
444 - </table>
626 + <div id="api-keys" class="mxchat-tab-content">
627 + <div class="mxchat-card">
628 + <div class="mxchat-autosave-section">
629 + <table class="form-table">
630 + <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
631 + </table>
632 + </div>
445 633 </div>
634 + </div>
446 635
447 - <div class="section-divider"></div>
636 + <div id="embed" class="mxchat-tab-content">
448 637
449 - <div class="mxchat-settings-section">
450 - <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
451 - <table class="form-table">
452 - <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
453 - </table>
638 + <div class="mxchat-card">
639 + <h2><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h2>
640 + <div class="mxchat-autosave-section">
641 + <table class="form-table">
642 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
643 + </table>
644 + </div>
454 645 </div>
455 646
456 - <div class="section-divider"></div>
457 647
458 - <div class="mxchat-settings-section">
459 - <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
460 - <table class="form-table">
461 - <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
462 - </table>
648 + <div class="mxchat-card">
649 + <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
650 + <div class="mxchat-autosave-section">
651 + <table class="form-table">
652 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
653 + </table>
654 + </div>
463 655 </div>
464 656
465 - <div class="section-divider"></div>
657 + <div class="mxchat-card">
658 + <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
659 + <div class="mxchat-autosave-section">
660 + <table class="form-table">
661 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
662 + </table>
663 + </div>
664 + </div>
466 665
467 - <!-- Live Agent Settings Section -->
468 - <div class="mxchat-settings-section">
469 - <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
470 - <p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('to set up live agent transfer via Slack.', 'mxchat'); ?></p>
471 - <table class="form-table">
472 - <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
473 - </table>
666 + <div class="mxchat-card">
667 + <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
668 + <div class="mxchat-autosave-section">
669 +<p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('for setup instructions and to see what\'s changed in the latest update.', 'mxchat'); ?></p> <table class="form-table">
670 + <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
671 + </table>
672 + </div>
474 673 </div>
475 674 </div>
476 - </div>
477 675
676 + <div id="general" class="mxchat-tab-content">
677 + <div class="mxchat-card">
678 + <?php do_settings_sections('mxchat-general'); ?>
679 + <div class="video-tutorials-section">
478 680
479 - <div id="general" class="mxchat-tab-content">
480 - <?php do_settings_sections('mxchat-general'); ?>
481 - <p>
482 - <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
483 - <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
484 - <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
485 - </p>
486 - <p>
487 - <?php echo esc_html__('If you like our plugin, please consider', 'mxchat'); ?> <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('leaving us a review', 'mxchat'); ?></a>.
488 - </p>
489 681
490 - <div class="faq-item">
491 - <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
492 - <p>
493 - <?php echo esc_html__('The Claude API, provided by Anthropic, allows for intelligent, context-aware chatbot responses in MxChat. To use it, you will need both an OpenAI API key and a Claude API key. This is necessary because the system utilizes OpenAI for vector embedding in the Retrieval-Augmented Generation (RAG) process, as Claude does not currently offer an embedding API.', 'mxchat'); ?>
494 - </p>
495 - <p>
496 - <?php echo esc_html__('When using the Claude API, your custom content is sent to Claude for generating responses, while the embeddings are processed through OpenAI. This ensures your chatbot provides accurate and engaging responses while maintaining knowledge relevant to your website.', 'mxchat'); ?>
497 - </p>
498 - <p>
499 - <?php echo esc_html__('You can obtain your Claude API key by signing up on the', 'mxchat'); ?> <a href="https://www.anthropic.com/api" target="_blank" rel="noopener"><?php echo esc_html__('Anthropic API page', 'mxchat'); ?></a>.
500 - </p>
501 - </div>
682 + <div class="support-notification">
683 + <div class="support-notification-icon">
684 + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
685 + <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
686 + <path d="M12 9v4"/>
687 + <path d="M12 17h.01"/>
688 + </svg>
689 + </div>
690 + <div class="support-notification-content">
691 + <h3 class="support-notification-title"><?php echo esc_html__('Need Help? We\'re Here for You!', 'mxchat'); ?></h3>
692 + <p class="support-notification-message">
693 + <?php echo esc_html__('Our goal is to provide the best experience and AI chatbot plugin available. If you\'re having trouble or believe something is not working as expected, please don\'t hesitate to submit a support ticket.', 'mxchat'); ?>
694 + </p>
695 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer" class="support-notification-button">
696 + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
697 + <path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/>
698 + <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/>
699 + </svg>
700 + <?php echo esc_html__('Submit Support Ticket', 'mxchat'); ?>
701 + </a>
702 + </div>
703 +</div>
502 704
503 - <div class="faq-item">
504 - <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
505 - <p>
506 - <?php echo esc_html__('The X.AI API, released on 10.21.24, is currently in beta. To use it, you will need both an OpenAI API key and an X.AI API key. This is because OpenAI handles vector embeddings in the Retrieval-Augmented Generation (RAG) process, as X.AI does not yet provide an embedding API.', 'mxchat'); ?>
507 - </p>
508 - <p>
509 - <?php echo esc_html__('Custom content is sent to the X.AI API for generating responses, while OpenAI processes the embeddings. This allows the chatbot to provide advanced responses while maintaining context from your website. You can get your X.AI API key from the', 'mxchat'); ?> <a href="https://docs.x.ai/docs" target="_blank" rel="noopener"><?php echo esc_html__('X.AI API documentation', 'mxchat'); ?></a>.
510 - </p>
511 - </div>
705 + <div class="tutorial-grid">
512 706
513 - <div class="faq-item">
514 - <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
515 - <p>
516 - <?php echo esc_html__('Yes, you will need an OpenAI API key to power the chatbot. You can obtain an API key by signing up on the', 'mxchat'); ?> <a href="https://platform.openai.com/signup" target="_blank" rel="noopener"><?php echo esc_html__('OpenAI platform', 'mxchat'); ?></a>. <?php echo esc_html__('After signing up, you must add credits to your account—typically, $5 in credits is sufficient to get started.', 'mxchat'); ?>
517 - </p>
518 - <p>
519 - <?php echo esc_html__('Once you have your API key, simply enter it in the chatbot\'s settings to enable functionality. The chatbot relies on OpenAI’s models for generating responses, so having sufficient credits in your OpenAI account is essential for smooth operation.', 'mxchat'); ?>
520 - </p>
707 + <div class="tutorial-item">
708 + <h3><?php echo esc_html__('AI Theme Generator Tutorial', 'mxchat'); ?></h3>
709 + <div class="video-description">
710 + <p><?php echo esc_html__('Learn how to instantly restyle your chatbot using plain English prompts. The AI Theme Generator lets you generate and apply beautiful designs with real-time previews—no CSS skills needed.', 'mxchat'); ?></p>
711 + <a href="https://www.youtube.com/watch?v=rSQDW2qbtRU&t" target="_blank" rel="noopener" class="video-link">
712 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
713 + <?php echo esc_html__('Watch AI Theme Generator Tutorial', 'mxchat'); ?>
714 + </a>
715 + </div>
716 +</div>
717 +
718 +
719 + <div class="tutorial-item">
720 + <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
721 + <div class="video-description">
722 + <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
723 + <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
724 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
725 + <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
726 + </a>
521 727 </div>
728 + </div>
522 729
523 - <div class="faq-item">
524 - <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
525 - <p>
526 - <?php echo esc_html__('You can add the chatbot using the shortcode', 'mxchat'); ?> <code>[mxchat_chatbot floating="yes"]</code> <?php echo esc_html__('or', 'mxchat'); ?> <code>[mxchat_chatbot floating="no"]</code>. <?php echo esc_html__('For initial testing and styling, it\'s best to use the shortcode on a draft or non-public page. Once you’re ready to go live, enable the “Append Chat Widget to Body” option in the settings for site-wide integration (recommended) or add shortcode to the footer.', 'mxchat'); ?>
527 - </p>
528 - </div>
730 +<div class="tutorial-item">
731 + <h3><?php echo esc_html__('Admin Assistant Add-on', 'mxchat'); ?></h3>
732 + <div class="video-description">
733 + <p><?php echo esc_html__('Discover how to use the MxChat Admin Assistant to bring a ChatGPT-like experience directly inside your WordPress dashboard. Learn to access multiple AI models, save conversations, generate images, and use web search - all without leaving your admin panel.', 'mxchat'); ?></p>
734 + <a href="https://youtu.be/AdEA1k-UCFM" target="_blank" rel="noopener" class="video-link">
735 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2-3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
736 + <?php echo esc_html__('Watch Admin Assistant Tutorial', 'mxchat'); ?>
737 + </a>
738 + </div>
739 +</div>
529 740
530 - <div class="faq-item">
531 - <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
532 - <p>
533 - <?php echo esc_html__('The chatbot uses AI and vector embeddings to connect users with relevant information. When you submit content, it converts it into a mathematical format. When a user asks a question, the bot matches it to your stored content and generates a response based on relevance. For example, submitting "Our phone number is 910-123-4567" allows the bot to retrieve this information when asked about contact details. To ensure related information is provided together, submit it in one entry.', 'mxchat'); ?>
534 - </p>
741 + <div class="tutorial-item">
742 + <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
743 + <div class="video-description">
744 + <p><?php echo esc_html__('Discover how to use the Intent Tester to fine-tune your chatbot\'s responses and ensure it accurately understands user queries.', 'mxchat'); ?></p>
745 + <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
746 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
747 + <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
748 + </a>
535 749 </div>
750 + </div>
536 751
537 - <div class="faq-item">
538 - <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
539 - <p>
540 - <?php echo esc_html__('Occasionally, the chatbot may generate inaccurate links or information, known as "hallucinations." To minimize this, you can add system instructions to guide its behavior. For example, include an instruction like: "Only provide links you directly have access to or retrieve from the knowledge base. Do not make up links or information that you do not have direct access to." This helps the bot stay aligned with your content.', 'mxchat'); ?>
541 - </p>
752 + <div class="tutorial-item">
753 + <h3><?php echo esc_html__('Theme Customizer Add-on', 'mxchat'); ?></h3>
754 + <div class="video-description">
755 + <p><?php echo esc_html__('Learn how to customize your chatbot appearance with the Theme Customizer add-on. Easily modify colors, fonts, and styles with real-time previews to match your brand perfectly.', 'mxchat'); ?></p>
756 + <a href="https://youtu.be/MfbB9mZi6ag" target="_blank" rel="noopener" class="video-link">
757 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
758 + <?php echo esc_html__('Watch Theme Customizer Tutorial', 'mxchat'); ?>
759 + </a>
542 760 </div>
761 + </div>
543 762
544 - <div class="faq-item">
545 - <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
546 - <p>
547 - <?php echo esc_html__('Intents allow MxChat to recognize user requests and trigger specific actions, such as capturing emails or displaying product information. This helps provide a more interactive and responsive experience. For a detailed explanation of each intent, please refer to our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Intents Documentation', 'mxchat'); ?></a>.
548 - </p>
763 + <div class="tutorial-item">
764 + <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
765 + <div class="video-description">
766 + <p><?php echo esc_html__('See how to integrate MxChat with your WooCommerce store to provide product recommendations and shopping assistance to your customers.', 'mxchat'); ?></p>
767 + <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
768 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
769 + <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
770 + </a>
549 771 </div>
772 + </div>
550 773
551 - <div class="faq-item">
552 - <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
553 - <p>
554 - <?php echo esc_html__('The Pro version offers direct integration with the Complianz GDPR plugin, making it easy to stay compliant with GDPR. If Complianz is enabled on your website, users must accept consent before the chatbot widget appears. The chatbot will only display once the user has accepted, ensuring compliance with data privacy regulations.', 'mxchat'); ?>
555 - </p>
774 + <div class="tutorial-item">
775 + <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
776 + <div class="video-description">
777 + <p><?php echo esc_html__('Learn how to set up your knowledge base using PDFs, sitemaps, and manual entries to enhance your chatbot\'s responses with site-specific information.', 'mxchat'); ?></p>
778 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
779 + <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
780 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
781 + <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
782 + </a>
556 783 </div>
784 + </div>
557 785
558 - <div class="faq-item">
559 - <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
560 - <p>
561 - <?php echo esc_html__('With WooCommerce integration, the chatbot automatically embeds new products and updates existing ones. For already-published products, you can click update or submit the product sitemap. The “Order History Access” setting allows the bot to access users\' order history (requires login) and assist with order inquiries. To enable the “Add to Cart” feature, add this system instruction: “After discussing a product, ask if the user wants to add it to their cart. The user must say \'Add to cart\' exactly.” Currently, this feature supports English only, with more languages coming soon.', 'mxchat'); ?>
562 - </p>
786 + <div class="tutorial-item">
787 + <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
788 + <div class="video-description">
789 + <p><?php echo esc_html__('See how to use the MxChat toolbar to chat with PDF and Word documents for enhanced document analysis and information retrieval.', 'mxchat'); ?></p>
790 + <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
791 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
792 + <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
793 + </a>
563 794 </div>
795 + </div>
564 796
565 - <div class="faq-item">
566 - <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
567 - <p>
568 - <?php echo esc_html__('AI chatbots can sometimes behave in unexpected ways, especially if you\'re new to configuring AI for specific tasks. We\'re committed to helping you get the most out of your chatbot experience. While we’re working on comprehensive guides and video tutorials, our team is here to assist you directly. If your chatbot isn\'t delivering the responses you need, please don’t hesitate to', 'mxchat'); ?> <a href="https://mxchat.ai/contact/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('contact us', 'mxchat'); ?></a> <?php echo esc_html__('for personalized support.', 'mxchat'); ?>
569 - </p>
570 - <p>
571 - <?php echo esc_html__('We’re dedicated to your success and ready to guide you in aligning the AI\'s behavior to meet your goals.', 'mxchat'); ?>
572 - </p>
797 + <div class="tutorial-item">
798 + <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
799 + <div class="video-description">
800 + <p><?php echo esc_html__('Learn how to integrate Perplexity with your chatbot for real-time web search capabilities. This tutorial covers intent recognition, the toolbar toggle button, and how to enable your chatbot to search the web and provide up-to-date information to your visitors.', 'mxchat'); ?></p>
801 + <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
802 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
803 + <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
804 + </a>
573 805 </div>
806 + </div>
574 807
575 - <div class="faq-item">
576 - <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
577 - <p>
578 - <?php echo esc_html__('Loops is a powerful SaaS email service that helps you automate and enhance your email marketing campaigns, making it easy to reach and engage with your audience. To integrate Loops with MxChat, you’ll need an API key from Loops. You can obtain this key by logging into your Loops account and navigating to the API settings. Visit the', 'mxchat'); ?> <a href="https://loops.so" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Loops website', 'mxchat'); ?></a> <?php echo esc_html__('to get started or to sign up for an account.', 'mxchat'); ?>
579 - </p>
808 + <div class="tutorial-item">
809 + <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
810 + <div class="video-description">
811 + <p><?php echo esc_html__('Learn how to leverage Brave Search intent capabilities to improve your chatbot\'s understanding of user queries and provide more accurate responses.', 'mxchat'); ?></p>
812 + <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
813 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
814 + <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
815 + </a>
580 816 </div>
581 -
582 817 </div>
583 - </div>
584 - <?php
585 -}
586 818
587 -
588 -
589 -public function mxchat_create_transcripts_page() {
590 - global $wpdb;
591 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
592 -
593 - // Get basic stats
594 - $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
595 - $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
596 - $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
597 - ?>
598 - <div class="wrap mxchat-admin">
599 - <!-- Stats Cards -->
600 - <div class="mxchat-stats-grid">
601 - <div class="mxchat-stat-card">
602 - <div class="stat-icon">💬</div>
603 - <div class="stat-content">
604 - <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
605 - <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
606 - </div>
819 + <div class="tutorial-item">
820 + <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
821 + <div class="video-description">
822 + <p><?php echo esc_html__('Discover how to set up email capture with MxChat using Loops to grow your mailing list while providing value through your chatbot.', 'mxchat'); ?></p>
823 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
824 + <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
825 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
826 + <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
827 + </a>
607 828 </div>
608 - <div class="mxchat-stat-card">
609 - <div class="stat-icon">📝</div>
610 - <div class="stat-content">
611 - <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
612 - <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
613 - </div>
614 - </div>
615 - <div class="mxchat-stat-card">
616 - <div class="stat-icon">👥</div>
617 - <div class="stat-content">
618 - <span class="stat-value"><?php echo esc_html($total_users); ?></span>
619 - <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
620 - </div>
621 - </div>
622 829 </div>
623 830
624 - <!-- Search and Filter Controls -->
625 - <div class="mxchat-controls-wrapper">
626 - <div class="mxchat-search-box">
627 - <input type="text" id="mxchat-search-transcripts"
628 - placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
629 - class="regular-text">
831 + <div class="tutorial-item">
832 + <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
833 + <div class="video-description">
834 + <p><?php echo esc_html__('Learn how to use the MxChat AI Agent Testing Service to evaluate and improve your chatbot\'s performance and accuracy.', 'mxchat'); ?></p>
835 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
836 + <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
837 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
838 + <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
839 + </a>
630 840 </div>
841 + </div>
842 + </div>
631 843
632 - <form id="mxchat-delete-form" method="post">
633 - <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
634 - <div class="mxchat-controls">
635 - <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
636 - <span class="dashicons dashicons-yes-alt"></span>
637 - <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
638 - </button>
639 - <button type="submit" class="button delete-chats-button">
640 - <span class="dashicons dashicons-trash"></span>
641 - <?php esc_html_e('Delete Selected', 'mxchat'); ?>
642 - </button>
844 + </div>
643 845 </div>
644 - </form>
846 + </div>
645 847 </div>
646 -
647 - <!-- Transcripts Container -->
648 - <div id="mxchat-transcripts"></div>
649 848 </div>
650 849 <?php
651 850 }
652 851
653 -public function mxchat_create_prompts_page() {
654 - global $wpdb;
655 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
852 +public function dismiss_live_agent_notice() {
853 + // Add debugging
854 + //error_log('dismiss_live_agent_notice called');
855 + //error_log('POST data: ' . print_r($_POST, true));
656 856
657 - // Display success message if all prompts were deleted
658 - if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
659 - echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
857 + // Verify nonce
858 + if (!wp_verify_nonce($_POST['nonce'], 'dismiss_live_agent_notice')) {
859 + //error_log('Nonce verification failed');
860 + wp_die('Security check failed');
660 861 }
661 862
662 - // Set up pagination and search query
663 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
664 - $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
665 - $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
666 - $per_page = 10;
667 - $offset = ($current_page - 1) * $per_page;
863 + // Remove the notice flag
864 + $deleted = delete_option('mxchat_show_live_agent_disabled_notice');
865 + //error_log('Option deleted: ' . ($deleted ? 'yes' : 'no'));
668 866
669 - // Modify query to handle search input
670 - $sql_search = "";
671 - if ($search_query) {
672 - $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
867 + wp_send_json_success();
868 +}
869 +public function add_live_agent_nonce() {
870 + if (get_option('mxchat_show_live_agent_disabled_notice', false)) {
871 + // Make sure your admin script is enqueued and localize the data
872 + wp_localize_script('mxchat-admin-js', 'mxchatLiveAgent', array(
873 + 'nonce' => wp_create_nonce('dismiss_live_agent_notice'),
874 + 'ajaxurl' => admin_url('admin-ajax.php')
875 + ));
673 876 }
877 +}
674 878
675 - // Retrieve total number of prompts, considering search filter
676 - $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
677 - $total_pages = ceil($total_prompts / $per_page);
678 879
679 - // Retrieve prompts from the database
680 - $prompts = $wpdb->get_results(
681 - $wpdb->prepare(
682 - "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
683 - $per_page,
684 - $offset
880 +public function mxchat_create_transcripts_page() {
881 + global $wpdb;
882 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
883 +
884 + // Get basic stats
885 + $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
886 + $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
887 +
888 + // Count unique users with detailed breakdown
889 + $total_users = $wpdb->get_var("
890 + SELECT COUNT(DISTINCT
891 + CASE
892 + WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
893 + WHEN user_id != 0 THEN CONCAT('user_', user_id)
894 + WHEN user_identifier NOT LIKE 'Tech-Savvy User'
895 + AND user_identifier NOT LIKE 'Detail-Oriented User'
896 + AND user_identifier NOT LIKE 'Language Learner'
897 + AND user_identifier NOT LIKE 'Casual Browser'
898 + AND user_identifier NOT LIKE 'Policy Enforcer'
899 + AND user_identifier NOT LIKE 'Researcher'
900 + AND user_identifier NOT LIKE 'Loyalty Member'
901 + AND user_identifier NOT LIKE 'Gift Buyer'
902 + AND user_identifier NOT LIKE 'Parent or Caregiver'
903 + THEN user_identifier
904 + ELSE session_id
905 + END
685 906 )
686 - );
907 + FROM $table_name
908 + WHERE role != 'assistant'
909 + ");
687 910
688 - // Add the pagination links generation here
689 - $page_links = paginate_links(array(
690 - 'base' => add_query_arg(array(
691 - 'paged' => '%#%',
692 - 'search' => urlencode($search_query),
693 - '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
694 - ), admin_url('admin.php?page=mxchat-prompts')),
695 - 'format' => '',
696 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
697 - 'next_text' => __('Next &raquo;', 'mxchat'),
698 - 'total' => $total_pages,
699 - 'current' => $current_page,
700 - ));
911 + // Get user type breakdown
912 + $registered_users = $wpdb->get_var("
913 + SELECT COUNT(DISTINCT user_email)
914 + FROM $table_name
915 + WHERE user_email != '' AND user_email IS NOT NULL
916 + ");
701 917
702 - // Retrieve processing statuses
703 - $pdf_url = get_transient('mxchat_last_pdf_url');
704 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
705 - $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
706 - $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
918 + $guest_users = $wpdb->get_var("
919 + SELECT COUNT(DISTINCT user_identifier)
920 + FROM $table_name
921 + WHERE (user_email = '' OR user_email IS NULL)
922 + AND role != 'assistant'
923 + AND user_identifier NOT LIKE 'Tech-Savvy User'
924 + AND user_identifier NOT LIKE 'Detail-Oriented User'
925 + AND user_identifier NOT LIKE 'Language Learner'
926 + AND user_identifier NOT LIKE 'Casual Browser'
927 + AND user_identifier NOT LIKE 'Policy Enforcer'
928 + AND user_identifier NOT LIKE 'Researcher'
929 + AND user_identifier NOT LIKE 'Loyalty Member'
930 + AND user_identifier NOT LIKE 'Gift Buyer'
931 + AND user_identifier NOT LIKE 'Parent or Caregiver'
932 + ");
707 933
708 - if ($pdf_status && $pdf_status['status'] === 'complete') {
709 - delete_transient('mxchat_last_pdf_url');
710 - $pdf_status = false;
711 - }
712 - if ($sitemap_status && $sitemap_status['status'] === 'complete') {
713 - delete_transient('mxchat_last_sitemap_url');
714 - $sitemap_status = false;
715 - }
716 -
717 - $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
718 - || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
934 + // Get agent test messages count
935 + $agent_tests = $wpdb->get_var("
936 + SELECT COUNT(DISTINCT session_id)
937 + FROM $table_name
938 + WHERE user_identifier IN (
939 + 'Tech-Savvy User',
940 + 'Detail-Oriented User',
941 + 'Language Learner',
942 + 'Casual Browser',
943 + 'Policy Enforcer',
944 + 'Researcher',
945 + 'Loyalty Member',
946 + 'Gift Buyer',
947 + 'Parent or Caregiver'
948 + )
949 + ");
719 950 ?>
720 - <div class="wrap mxchat-wrapper">
951 + <div class="wrap mxchat-transcripts-wrapper">
721 952 <!-- Hero Section -->
722 - <div class="mxchat-hero">
953 + <div class="mxchat-transcripts-hero">
723 954 <h1 class="mxchat-main-title">
724 - <span class="mxchat-gradient-text">Knowledge Base</span> Manager
955 + Chat <span class="mxchat-gradient-text">Transcripts</span>
725 956 </h1>
726 957 <p class="mxchat-hero-subtitle">
727 - <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
958 + <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
728 959 </p>
729 960 </div>
730 -
731 961 <div class="mxchat-content">
732 - <!-- Import Settings Card -->
733 - <div class="mxchat-card">
734 - <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
735 - <p><?php esc_html_e('Using the free Pinecone DB Manager add-on is recommended for high performance and a knowledge database of more than 500.', 'mxchat'); ?></p>
736 -
737 - <!-- Tab Contents -->
738 - <div class="mxchat-tab-contents">
739 - <!-- Default Database Tab -->
740 - <div id="default-db" class="mxchat-tab-content active">
741 - <!-- Auto-Sync Settings -->
742 - <div class="mxchat-settings-section">
743 - <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
744 - <p class="mxchat-description">
745 - <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below. Works with Pinecone if enabled', 'mxchat'); ?>
746 - </p>
747 - <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
748 - <div class="mxchat-toggle-group">
749 - <div class="mxchat-toggle-container">
750 - <label class="mxchat-toggle-switch">
751 - <input type="checkbox"
752 - name="mxchat_auto_sync_posts"
753 - class="mxchat-autosave-field"
754 - value="1"
755 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
756 - <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
757 - <span class="mxchat-toggle-slider"></span>
758 - </label>
759 - <span class="mxchat-toggle-label">
760 - <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
761 - </span>
762 - </div>
763 - <div class="mxchat-toggle-container">
764 - <label class="mxchat-toggle-switch">
765 - <input type="checkbox"
766 - name="mxchat_auto_sync_pages"
767 - class="mxchat-autosave-field"
768 - value="1"
769 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
770 - <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
771 - <span class="mxchat-toggle-slider"></span>
772 - </label>
773 - <span class="mxchat-toggle-label">
774 - <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
775 - </span>
776 - </div>
777 - </div>
778 - </div>
962 + <!-- Stats Cards -->
963 + <div class="mxchat-stats-grid">
964 + <div class="mxchat-stat-card">
965 + <div class="stat-icon">💬</div>
966 + <div class="stat-content">
967 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
968 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
779 969 </div>
780 -
781 - <!-- Import Methods -->
782 - <div class="mxchat-import-methods">
783 - <div class="mxchat-method-card">
784 - <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
785 - <p><?php esc_html_e('Import all content from your sitemap automatically.', 'mxchat'); ?></p>
786 - </div>
787 - <div class="mxchat-method-card">
788 - <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
789 - <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
790 - </div>
791 - <div class="mxchat-method-card">
792 - <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
793 - <p><?php esc_html_e('Import content from any specific webpage.', 'mxchat'); ?></p>
794 - </div>
795 - <div class="mxchat-method-card">
796 - <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
797 - <p><?php esc_html_e('Directly submit content to be vectorized.', 'mxchat'); ?></p>
798 - </div>
799 - </div>
800 -
801 - <!-- Import Form -->
802 - <?php if (!$is_processing) : ?>
803 - <div class="mxchat-import-form">
804 - <form id="mxchat-sitemap-form" method="post"
805 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
806 - <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
807 - <div class="mxchat-url-input-group">
808 - <input type="url"
809 - name="sitemap_url"
810 - id="sitemap_url"
811 - placeholder="<?php esc_attr_e('Enter Sitemap, PDF, or Webpage URL', 'mxchat'); ?>"
812 - required />
813 - <button type="submit"
814 - name="submit_sitemap"
815 - class="mxchat-button-primary">
816 - <?php esc_html_e('Import', 'mxchat'); ?>
817 - </button>
818 - </div>
819 - </form>
820 - </div>
821 - <?php endif; ?>
822 -
823 - <!-- Processing Status -->
824 - <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
825 - <div class="mxchat-status-card">
826 - <div class="mxchat-status-header">
827 - <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
828 - <?php if ($is_processing) : ?>
829 - <form method="post" class="mxchat-stop-form"
830 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
831 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
832 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
833 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
834 - </button>
835 - </form>
836 - <?php endif; ?>
837 - </div>
838 - <div class="mxchat-progress-bar">
839 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
840 - </div>
841 - <div class="mxchat-status-details">
842 - <p><?php printf(
843 - esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
844 - absint($pdf_status['processed_pages']),
845 - absint($pdf_status['total_pages']),
846 - absint($pdf_status['percentage'])
847 - ); ?></p>
848 - <p><?php printf(
849 - esc_html__('Status: %s', 'mxchat'),
850 - esc_html(ucfirst($pdf_status['status']))
851 - ); ?></p>
852 - <p><?php printf(
853 - esc_html__('Last update: %s', 'mxchat'),
854 - esc_html($pdf_status['last_update'])
855 - ); ?></p>
856 - </div>
857 - </div>
858 - <?php endif; ?>
859 -
860 - <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
861 - <div class="mxchat-status-card">
862 - <div class="mxchat-status-header">
863 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
864 - <?php if ($is_processing) : ?>
865 - <form method="post" class="mxchat-stop-form"
866 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
867 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
868 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
869 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
870 - </button>
871 - </form>
872 - <?php endif; ?>
873 - </div>
874 - <div class="mxchat-progress-bar">
875 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
876 - </div>
877 - <div class="mxchat-status-details">
878 - <p><?php printf(
879 - esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
880 - absint($sitemap_status['processed_urls']),
881 - absint($sitemap_status['total_urls']),
882 - absint($sitemap_status['percentage'])
883 - ); ?></p>
884 - <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
885 - <div class="mxchat-error-notice">
886 - <?php if (!empty($sitemap_status['error'])) : ?>
887 - <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
888 - <?php endif; ?>
889 - <?php if (!empty($sitemap_status['last_error'])) : ?>
890 - <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
891 - <?php endif; ?>
892 - </div>
893 - <?php endif; ?>
894 - </div>
895 - </div>
896 - <?php endif; ?>
970 + </div>
971 + <div class="mxchat-stat-card">
972 + <div class="stat-icon">📝</div>
973 + <div class="stat-content">
974 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
975 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
897 976 </div>
977 + </div>
978 + <div class="mxchat-stat-card">
979 + <div class="stat-icon">👥</div>
980 + <div class="stat-content">
981 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
982 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
983 + <span class="stat-sublabel">
984 + <?php
985 + echo sprintf(
986 + esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
987 + $registered_users,
988 + $guest_users,
989 + $agent_tests
990 + );
991 + ?>
992 + </span>
993 + </div>
994 + </div>
995 + </div>
898 996
899 -
997 + <!-- Search and Filter Controls with Notification Settings Button -->
998 + <div class="mxchat-controls-wrapper">
999 + <div class="mxchat-search-box">
1000 + <input type="text" id="mxchat-search-transcripts"
1001 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
1002 + class="regular-text">
900 1003 </div>
1004 + <form id="mxchat-delete-form" method="post">
1005 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
1006 + <div class="mxchat-controls">
1007 + <button type="button" id="mxchat-chat-email-notification-btn" class="mxchat-action-button">
1008 + <span class="dashicons dashicons-email"></span>
1009 + <?php esc_html_e('Notification Settings', 'mxchat'); ?>
1010 + </button>
1011 + <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
1012 + <span class="dashicons dashicons-download"></span>
1013 + <?php esc_html_e('Export All Chats', 'mxchat'); ?>
1014 + </button>
1015 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
1016 + <span class="dashicons dashicons-yes-alt"></span>
1017 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
1018 + </button>
1019 + <button type="submit" class="button delete-chats-button">
1020 + <span class="dashicons dashicons-trash"></span>
1021 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
1022 + </button>
1023 + </div>
1024 + </form>
901 1025 </div>
902 1026
903 - <!-- Direct Content Submission Card -->
904 -<div class="mxchat-card">
905 - <div class="mxchat-card-header">
906 - <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
1027 + <!-- Transcripts Container -->
1028 + <div id="mxchat-transcripts"></div>
1029 + </div>
907 1030 </div>
908 - <form id="mxchat-content-form" method="post"
909 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
910 - <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
911 - <div class="mxchat-form-group">
912 - <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
913 - <textarea name="article_content" id="article_content" required
914 - placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
915 - </div>
916 - <div class="mxchat-form-group">
917 - <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
918 - <input type="url" name="article_url" id="article_url"
919 - placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
920 - </div>
921 - <button type="submit" name="submit_content" class="mxchat-button-primary">
922 - <?php esc_html_e('Submit Content', 'mxchat'); ?>
923 - </button>
924 - </form>
925 -</div>
926 1031
927 -<!-- Knowledge Base Table Card -->
928 -<div class="mxchat-card">
929 - <div class="mxchat-card-header">
930 - <h2><?php esc_html_e('Knowledge Base', 'mxchat'); ?></h2>
931 - <div class="mxchat-header-actions">
932 - <!-- Search -->
933 - <form method="get" id="knowledge-search" class="mxchat-search-form">
934 - <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
935 - <input type="hidden" name="page" value="mxchat-prompts" />
936 - <div class="mxchat-search-group">
937 - <span class="dashicons dashicons-search"></span>
938 - <input type="text"
939 - name="search"
940 - placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
941 - value="<?php echo esc_attr($search_query); ?>" />
942 - </div>
943 - </form>
944 -
945 - <!-- Delete All -->
946 - <form method="post"
947 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
948 - class="mxchat-delete-form"
949 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
950 - <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
951 - <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
952 - <span class="dashicons dashicons-trash"></span>
953 - <?php esc_html_e('Delete All', 'mxchat'); ?>
954 - </button>
955 - </form>
1032 + <!-- Modal for Chat Email Notification Settings -->
1033 + <div id="mxchat-chat-email-notification-modal" class="mxchat-chat-notification-modal-overlay" style="display: none;">
1034 + <div class="mxchat-chat-notification-modal-content">
1035 + <div class="mxchat-chat-notification-modal-header">
1036 + <h2><?php esc_html_e('Email Notification Settings', 'mxchat'); ?></h2>
1037 + <button type="button" class="mxchat-chat-notification-modal-close">&times;</button>
1038 + </div>
1039 + <div class="mxchat-chat-notification-modal-body">
1040 + <form method="post" action="options.php" id="mxchat-chat-email-notification-form">
1041 + <?php
1042 + settings_fields('mxchat_transcripts_options');
1043 + do_settings_sections('mxchat-transcripts');
1044 + ?>
1045 + <div class="mxchat-chat-notification-modal-footer">
1046 + <button type="submit" class="button button-primary">
1047 + <?php esc_html_e('Save Notification Settings', 'mxchat'); ?>
1048 + </button>
1049 + <button type="button" class="button mxchat-chat-notification-modal-cancel">
1050 + <?php esc_html_e('Cancel', 'mxchat'); ?>
1051 + </button>
1052 + </div>
1053 + </form>
1054 + </div>
956 1055 </div>
957 1056 </div>
1057 + <?php
1058 +}
958 1059
959 - <!-- Table -->
960 - <div class="mxchat-table-wrapper">
961 - <table class="mxchat-records-table">
962 - <thead>
963 - <tr>
964 - <th><?php esc_html_e('ID', 'mxchat'); ?></th>
965 - <th><?php esc_html_e('Content', 'mxchat'); ?></th>
966 - <th><?php esc_html_e('Source', 'mxchat'); ?></th>
967 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
968 - </tr>
969 - </thead>
970 - <tbody>
971 - <?php if ($prompts) : ?>
972 - <?php foreach ($prompts as $prompt) : ?>
973 - <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
974 - <td><?php echo esc_html($prompt->id); ?></td>
975 - <td class="mxchat-content-cell">
976 - <div class="content-view">
977 - <?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?>
978 - </div>
979 - <textarea class="content-edit" style="display:none;">
980 - <?php echo esc_textarea($prompt->article_content); ?>
981 - </textarea>
982 - </td>
983 - <td class="mxchat-url-cell">
984 - <?php if (!empty($prompt->source_url)) : ?>
985 - <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
986 - <span class="dashicons dashicons-external"></span>
987 - <?php esc_html_e('View Source', 'mxchat'); ?>
988 - </a>
989 - <?php else : ?>
990 - <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
991 - <?php endif; ?>
992 - </td>
993 - <td class="mxchat-actions-cell">
994 - <button class="mxchat-button-icon edit-button"
995 - data-id="<?php echo esc_attr($prompt->id); ?>">
996 - <span class="dashicons dashicons-edit"></span>
997 - </button>
998 - <button class="mxchat-button-icon save-button"
999 - data-id="<?php echo esc_attr($prompt->id); ?>"
1000 - style="display:none;"
1001 - data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
1002 - <span class="dashicons dashicons-saved"></span>
1003 - </button>
1004 - <a href="<?php echo esc_url(admin_url(
1005 - 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1006 - . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1007 - )); ?>"
1008 - class="mxchat-button-icon delete-button"
1009 - onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1010 - <span class="dashicons dashicons-trash"></span>
1011 - </a>
1012 - </td>
1013 - </tr>
1014 - <?php endforeach; ?>
1015 - <?php else : ?>
1016 - <tr>
1017 - <td colspan="4" class="mxchat-no-records">
1018 - <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1019 - </td>
1020 - </tr>
1021 - <?php endif; ?>
1022 - </tbody>
1023 - </table>
1024 - </div>
1025 1060
1026 - <?php if ($page_links) : ?>
1027 - <div class="mxchat-pagination">
1028 - <?php echo wp_kses_post($page_links); ?>
1029 - </div>
1030 - <?php endif; ?>
1031 -</div>
1032 -</div>
1033 -</div>
1034 -<?php
1061 +public function mxchat_transcripts_notification_section_callback() {
1062 + echo '<p>' . esc_html__('Configure email notifications for new chat transcripts. You will receive an email notification when a new chat session begins.', 'mxchat') . '</p>';
1035 1063 }
1064 +public function mxchat_enable_notifications_callback() {
1065 + $options = get_option('mxchat_transcripts_options', array());
1066 + $enabled = isset($options['mxchat_enable_notifications']) ? $options['mxchat_enable_notifications'] : 0;
1067 + ?>
1068 + <label for="mxchat_enable_notifications">
1069 + <input type="checkbox" id="mxchat_enable_notifications"
1070 + name="mxchat_transcripts_options[mxchat_enable_notifications]"
1071 + value="1" <?php checked(1, $enabled); ?>>
1072 + <?php esc_html_e('Send email notification when a new chat session starts', 'mxchat'); ?>
1073 + </label>
1074 + <p class="description">
1075 + <?php esc_html_e('Enable this option to receive email notifications for new chat sessions.', 'mxchat'); ?>
1076 + </p>
1077 + <?php
1078 +}
1079 +public function mxchat_notification_email_callback() {
1080 + $options = get_option('mxchat_transcripts_options', array());
1081 + $email = isset($options['mxchat_notification_email']) ? $options['mxchat_notification_email'] : get_option('admin_email');
1082 + ?>
1083 + <input type="email" id="mxchat_notification_email"
1084 + name="mxchat_transcripts_options[mxchat_notification_email]"
1085 + value="<?php echo esc_attr($email); ?>"
1086 + class="regular-text">
1087 + <p class="description">
1088 + <?php esc_html_e('Enter the email address where notifications should be sent. Defaults to the admin email address.', 'mxchat'); ?>
1089 + </p>
1090 + <?php
1091 +}
1092 +public function sanitize_transcripts_options($input) {
1093 + $sanitized = array();
1036 1094
1095 + $sanitized['mxchat_enable_notifications'] = isset($input['mxchat_enable_notifications']) ? 1 : 0;
1037 1096
1038 -public function mxchat_handle_delete_all_prompts() {
1039 - // Verify nonce
1040 - if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
1041 - wp_die(__('Nonce verification failed.', 'mxchat'));
1097 + if (isset($input['mxchat_notification_email'])) {
1098 + $sanitized['mxchat_notification_email'] = sanitize_email($input['mxchat_notification_email']);
1099 + if (!is_email($sanitized['mxchat_notification_email'])) {
1100 + add_settings_error(
1101 + 'mxchat_transcripts_options',
1102 + 'invalid_email',
1103 + __('Please enter a valid email address for notifications.', 'mxchat'),
1104 + 'error'
1105 + );
1106 + $sanitized['mxchat_notification_email'] = get_option('admin_email');
1107 + }
1042 1108 }
1043 1109
1044 - // Check permissions
1110 + return $sanitized;
1111 +}
1112 +public function export_chat_transcripts() {
1045 1113 if (!current_user_can('manage_options')) {
1046 - wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
1114 + wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
1047 1115 }
1048 1116
1117 + check_ajax_referer('mxchat_export_transcripts', 'security');
1118 +
1049 1119 global $wpdb;
1050 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1120 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1051 1121
1052 - // Delete all prompts from the table
1053 - $wpdb->query("DELETE FROM {$table_name}");
1122 + // Get all transcripts ordered by session and timestamp
1123 + $results = $wpdb->get_results(
1124 + "SELECT session_id, user_email, user_identifier, role, message, timestamp
1125 + FROM {$table_name}
1126 + ORDER BY session_id, timestamp ASC"
1127 + );
1054 1128
1055 - // Clear relevant cache
1056 - wp_cache_delete('all_prompts', 'mxchat_prompts');
1129 + if (empty($results)) {
1130 + wp_send_json_error(array('message' => 'No transcripts found.'));
1131 + wp_die();
1132 + }
1057 1133
1058 - // Redirect back with a success message
1059 - $redirect_url = add_query_arg(array(
1060 - 'page' => 'mxchat-prompts',
1061 - 'all_deleted' => 'true'
1062 - ), admin_url('admin.php'));
1134 + // Set headers for CSV download
1135 + header('Content-Type: text/csv');
1136 + header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
1137 + header('Pragma: no-cache');
1138 + header('Expires: 0');
1063 1139
1064 - wp_safe_redirect($redirect_url);
1065 - exit;
1066 -}
1067 -public function mxchat_handle_delete_prompt() {
1068 - // Sanitize and validate nonce
1069 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
1070 - if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
1071 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1072 - }
1140 + // Create output stream
1141 + $output = fopen('php://output', 'w');
1073 1142
1074 - // Check permissions
1075 - if (!current_user_can('manage_options')) {
1076 - wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
1077 - }
1143 + // Add UTF-8 BOM for proper Excel encoding
1144 + fputs($output, "\xEF\xBB\xBF");
1078 1145
1079 - // Validate and sanitize ID parameter
1080 - $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
1081 - if ($id <= 0) {
1082 - wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
1146 + // Add CSV headers
1147 + fputcsv($output, array(
1148 + 'Session ID',
1149 + 'Email',
1150 + 'User Identifier',
1151 + 'Role',
1152 + 'Message',
1153 + 'Timestamp'
1154 + ));
1155 +
1156 + // Add data rows
1157 + foreach ($results as $row) {
1158 + fputcsv($output, array(
1159 + $row->session_id,
1160 + $row->user_email,
1161 + $row->user_identifier,
1162 + $row->role,
1163 + $row->message,
1164 + $row->timestamp
1165 + ));
1083 1166 }
1084 1167
1085 - global $wpdb;
1086 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1087 -
1088 - // Clear cache and delete prompt
1089 - wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
1090 - $wpdb->delete($table_name, array('id' => $id), array('%d'));
1091 -
1092 - wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1093 - exit;
1168 + fclose($output);
1169 + wp_die();
1094 1170 }
1095 -public function mxchat_generate_embedding($text) {
1096 - $options = get_option('mxchat_options');
1097 - $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1098 1171
1099 - $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1100 - 'body' => wp_json_encode(array(
1101 - 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1102 - 'input' => $text
1103 - )),
1104 - 'headers' => array(
1105 - 'Authorization' => 'Bearer ' . $api_key,
1106 - 'Content-Type' => esc_html__('application/json', 'mxchat')
1107 - ),
1108 - ));
1109 1172
1110 - if (is_wp_error($response)) {
1111 - return null;
1112 - }
1173 +public function mxchat_fetch_chat_history() {
1174 + global $wpdb;
1175 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1176 + $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1113 1177
1114 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1115 - return $response_data['data'][0]['embedding'] ?? null;
1178 + if (!current_user_can('manage_options')) {
1179 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1116 1180 }
1117 -public function mxchat_delete_chat_history() {
1118 - if (!current_user_can('manage_options')) {
1119 - echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1120 - wp_die();
1121 - }
1122 1181
1123 - check_ajax_referer('mxchat_delete_chat_history', 'security');
1182 + $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1183 + $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
1184 + $offset = ($page - 1) * $per_page;
1185 + $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
1124 1186
1125 - global $wpdb;
1126 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1187 + $search_condition = '';
1188 + $search_params = [];
1127 1189
1128 - if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
1129 - foreach ($_POST['delete_session_ids'] as $session_id) {
1130 - $session_id_sanitized = sanitize_text_field($session_id);
1190 + if (!empty($search)) {
1191 + $search_condition = "WHERE (
1192 + session_id LIKE %s
1193 + OR user_email LIKE %s
1194 + OR user_name LIKE %s
1195 + OR user_identifier LIKE %s
1196 + OR message LIKE %s
1197 + )";
1198 + $search_params = array_fill(0, 5, '%' . $wpdb->esc_like($search) . '%');
1199 + }
1131 1200
1132 - // Clear relevant cache before deletion
1133 - $cache_key = 'chat_session_' . $session_id_sanitized;
1134 - wp_cache_delete($cache_key, 'mxchat_chat_sessions');
1201 + $count_query = !empty($search)
1202 + ? $wpdb->prepare("SELECT COUNT(DISTINCT session_id) FROM {$table_name} {$search_condition}", $search_params)
1203 + : "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
1135 1204
1136 - // Perform the deletion
1137 - $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
1138 - }
1205 + $total_sessions = $wpdb->get_var($count_query);
1139 1206
1140 - // Optionally, clear a general cache if you have one
1141 - wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
1207 + $session_query = !empty($search)
1208 + ? $wpdb->prepare(
1209 + "SELECT DISTINCT t.session_id FROM {$table_name} t {$search_condition}
1210 + GROUP BY t.session_id ORDER BY MAX(t.timestamp) DESC LIMIT %d OFFSET %d",
1211 + array_merge($search_params, [$per_page, $offset])
1212 + )
1213 + : $wpdb->prepare(
1214 + "SELECT DISTINCT session_id FROM {$table_name}
1215 + GROUP BY session_id ORDER BY MAX(timestamp) DESC LIMIT %d OFFSET %d",
1216 + $per_page, $offset
1217 + );
1142 1218
1143 - echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]);
1144 - } else {
1145 - echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
1219 + $session_ids = $wpdb->get_col($session_query);
1220 +
1221 + if (empty($session_ids)) {
1222 + ob_start();
1223 + echo '<div class="mxchat-no-results">';
1224 + echo esc_html__('No chat history found.', 'mxchat');
1225 + if (!empty($search)) {
1226 + echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
1146 1227 }
1147 -
1228 + echo '</div>';
1229 + wp_send_json([
1230 + 'html' => ob_get_clean(),
1231 + 'page' => $page,
1232 + 'total_pages' => 0,
1233 + 'total_sessions' => 0
1234 + ]);
1148 1235 wp_die();
1149 1236 }
1150 1237
1238 + $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
1239 + $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
1151 1240
1152 -public function mxchat_save_inline_prompt() {
1153 - // Check for nonce security
1154 - check_ajax_referer('mxchat_save_inline_nonce');
1241 + ob_start();
1242 + echo '<div class="mxchat-transcript">';
1155 1243
1156 - // Verify permissions
1157 - if (!current_user_can('manage_options')) {
1158 - wp_send_json_error(esc_html__('Permission denied.', 'mxchat'));
1159 - return;
1160 - }
1244 + foreach ($session_ids as $session_id) {
1245 + $session_data = $originating_columns_exist
1246 + ? $wpdb->get_row($wpdb->prepare(
1247 + "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
1248 + FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
1249 + $session_id
1250 + ))
1251 + : $wpdb->get_row($wpdb->prepare(
1252 + "SELECT user_email, user_name, user_identifier, timestamp FROM {$table_name}
1253 + WHERE session_id = %s LIMIT 1",
1254 + $session_id
1255 + ));
1161 1256
1162 - global $wpdb;
1163 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1257 + $clicked_urls = [];
1258 + if ($url_table_exists) {
1259 + $url_clicks = $wpdb->get_results(
1260 + $wpdb->prepare(
1261 + "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
1262 + WHERE session_id = %s ORDER BY click_timestamp ASC",
1263 + $session_id
1264 + )
1265 + );
1266 + foreach ($url_clicks as $click) {
1267 + $clicked_urls[] = $click->clicked_url;
1268 + }
1269 + }
1164 1270
1165 - // Validate and sanitize input data
1166 - $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1167 - $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1168 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1271 + $messages = $wpdb->get_results(
1272 + $wpdb->prepare(
1273 + "SELECT * FROM {$table_name}
1274 + WHERE session_id = %s ORDER BY timestamp ASC",
1275 + $session_id
1276 + )
1277 + );
1169 1278
1170 - if ($prompt_id > 0 && !empty($article_content)) {
1171 - // Re-generate the embedding vector for the updated content
1172 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1279 + $latest_timestamp = !empty($messages) ? end($messages)->timestamp : $session_data->timestamp;
1280 + $session_time = wp_date('M j, g:ia', strtotime($latest_timestamp . ' UTC'));
1173 1281
1174 - if (is_array($embedding_vector)) {
1175 - // Serialize the embedding vector before storing it
1176 - $embedding_vector_serialized = serialize($embedding_vector);
1282 + $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
1283 + $user_name = !empty($session_data->user_name) ? $session_data->user_name : ''; // NEW
1284 + $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
1177 1285
1178 - // Update the prompt in the database
1179 - $updated = $wpdb->update(
1180 - $table_name,
1181 - array(
1182 - 'article_content' => $article_content,
1183 - 'embedding_vector' => $embedding_vector_serialized,
1184 - 'source_url' => $article_url,
1185 - ),
1186 - array('id' => $prompt_id),
1187 - array('%s', '%s', '%s'),
1188 - array('%d')
1189 - );
1190 -
1191 - if ($updated !== false) {
1192 - wp_send_json_success();
1193 - } else {
1194 - wp_send_json_error(esc_html__('Database update failed.', 'mxchat'));
1286 + // Build user display with name if available
1287 + if (!empty($user_email)) {
1288 + $user_display = $user_email;
1289 + if (!empty($user_name)) {
1290 + $user_display .= ' (' . $user_name . ')';
1195 1291 }
1196 1292 } else {
1197 - wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat'));
1293 + $user_display = $user_identifier;
1198 1294 }
1199 - } else {
1200 - wp_send_json_error(esc_html__('Invalid data.', 'mxchat'));
1201 - }
1202 -}
1295 + echo '<div class="mxchat-session">';
1296 + echo '<div class="mxchat-session-header" data-session-id="' . esc_attr($session_id) . '">';
1297 + echo '<div class="mxchat-user-row">' . esc_html($user_display) . '</div>';
1203 1298
1299 + if ($originating_columns_exist && !empty($session_data->originating_page_url)) {
1300 + $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : $session_data->originating_page_url;
1204 1301
1302 + echo '<div class="mxchat-header-row flex-between">';
1303 + echo '<div class="mxchat-main-info">';
1304 + echo '<a href="' . esc_url($session_data->originating_page_url) . '" target="_blank" class="mxchat-page-link">';
1305 + echo esc_html($page_title);
1306 + echo '</a>';
1307 + echo '<span class="mxchat-session-time">' . esc_html($session_time) . '</span>';
1308 + echo '</div>';
1309 + echo '</div>';
1310 + }
1205 1311
1206 -// Add this method to handle post updates
1207 -public function handle_post_update($post_id, $post, $update) {
1208 - // Basic validation checks
1209 - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1210 - return;
1211 - }
1312 + if (!empty($clicked_urls)) {
1313 + echo '<div class="mxchat-links-row">';
1314 + echo '<span class="mxchat-label">Clicked:</span> ';
1315 + $link_count = 0;
1316 + foreach ($clicked_urls as $url) {
1317 + if ($link_count > 0) echo ', ';
1318 + echo '<a href="' . esc_url($url) . '" target="_blank" class="mxchat-link-item">' . esc_html(parse_url($url, PHP_URL_HOST)) . '</a>';
1319 + $link_count++;
1320 + if ($link_count >= 3) {
1321 + if (count($clicked_urls) > 3) {
1322 + echo ' +' . (count($clicked_urls) - 3) . ' more';
1323 + }
1324 + break;
1325 + }
1326 + }
1327 + echo '</div>';
1328 + }
1212 1329
1213 - // Only process published content
1214 - if (!in_array($post->post_status, array('publish'))) {
1215 - return;
1216 - }
1330 + echo '</div>'; // end session-header
1331 + echo '<div class="mxchat-messages">';
1217 1332
1218 - // Check if sync is enabled for this post type
1219 - $post_type = $post->post_type;
1220 - if (!in_array($post_type, array('post', 'page'))) {
1221 - return;
1222 - }
1333 + foreach ($messages as $transcript) {
1334 + $formatted_timestamp = wp_date('F j, Y g:i a', strtotime($transcript->timestamp . ' UTC'));
1223 1335
1224 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1225 - if (get_option($sync_option) != '1') {
1226 - return;
1227 - }
1336 + switch ($transcript->role) {
1337 + case 'assistant':
1338 + case 'bot':
1339 + $message_class = 'bot-message';
1340 + $display_role = esc_html__('Chatbot', 'mxchat');
1341 + break;
1342 + case 'user':
1343 + $message_class = 'user-message';
1344 + $display_role = !empty($transcript->user_identifier)
1345 + ? sanitize_text_field($transcript->user_identifier)
1346 + : esc_html__('User', 'mxchat');
1347 + break;
1348 + case 'agent':
1349 + $message_class = 'agent-message';
1350 + $display_role = esc_html__('Agent', 'mxchat');
1351 + break;
1352 + default:
1353 + $message_class = 'unknown-message';
1354 + $display_role = esc_html__('Unknown', 'mxchat');
1355 + }
1228 1356
1229 - // Prepare content and URL
1230 - $content = wp_strip_all_tags($post->post_content);
1231 - $url = get_permalink($post_id);
1357 + $message_content = wp_kses(
1358 + stripslashes($transcript->message),
1359 + [
1360 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1361 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1362 + 'a' => ['href' => [], 'title' => [], 'target' => []]
1363 + ]
1364 + );
1365 + $message_content = nl2br($message_content);
1232 1366
1233 - // Generate embedding vector
1234 - $embedding_vector = $this->mxchat_generate_embedding($content);
1235 - if (!$embedding_vector) {
1236 - return;
1367 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1368 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1369 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1370 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1371 + echo '</div>';
1372 + }
1373 +
1374 + echo '</div></div>'; // end messages and session
1237 1375 }
1238 1376
1239 - // Check for Pinecone addon and its settings
1240 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1241 - $use_pinecone = false;
1377 + echo '</div>'; // end transcript
1242 1378
1243 - if ($pinecone_settings && is_array($pinecone_settings)) {
1244 - // Check if Pinecone is enabled and all required settings are present
1245 - $use_pinecone = (
1246 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1247 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1248 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1249 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1250 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1251 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1252 - );
1253 - }
1379 + $total_pages = ceil($total_sessions / $per_page);
1254 1380
1255 - if ($use_pinecone) {
1256 - // Use Pinecone
1257 - $pinecone_result = $this->store_in_pinecone_main(
1258 - $embedding_vector,
1259 - $content,
1260 - $url,
1261 - $pinecone_settings['mxchat_pinecone_api_key'],
1262 - $pinecone_settings['mxchat_pinecone_environment'],
1263 - $pinecone_settings['mxchat_pinecone_index']
1264 - );
1381 + echo '<div class="mxchat-pagination">';
1382 + echo '<div class="mxchat-pagination-info">';
1383 + echo sprintf(
1384 + esc_html__('Showing %1$d to %2$d of %3$d sessions', 'mxchat'),
1385 + $offset + 1,
1386 + min($offset + $per_page, $total_sessions),
1387 + $total_sessions
1388 + );
1389 + echo '</div>';
1265 1390
1266 - if (!$pinecone_result['success']) {
1267 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1268 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1391 + if ($total_pages > 1) {
1392 + echo '<div class="mxchat-pagination-controls">';
1393 + if ($page > 1) {
1394 + echo '<button class="mxchat-pagination-button" data-page="' . ($page - 1) . '">&laquo; ' . esc_html__('Previous', 'mxchat') . '</button>';
1269 1395 }
1270 - } else {
1271 - // Fallback to WordPress DB storage
1272 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1273 - }
1274 -}
1275 -// Modified storage function to add type field
1276 -private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1277 - $vector_id = $vector_id ?: md5($url);
1278 - $options = get_option('mxchat_pinecone_addon_options');
1279 - $host = $options['mxchat_pinecone_host'] ?? '';
1280 1396
1281 - if (empty($host)) {
1282 - return array(
1283 - 'success' => false,
1284 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1285 - );
1286 - }
1397 + $start_page = max(1, $page - 2);
1398 + $end_page = min($total_pages, $page + 2);
1287 1399
1288 - // Determine if this is a product URL
1289 - $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1400 + if ($start_page > 1) {
1401 + echo '<button class="mxchat-pagination-button" data-page="1">1</button>';
1402 + if ($start_page > 2) {
1403 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1404 + }
1405 + }
1290 1406
1291 - $api_endpoint = "https://{$host}/vectors/upsert";
1292 - $request_body = array(
1293 - 'vectors' => array(
1294 - array(
1295 - 'id' => $vector_id,
1296 - 'values' => $embedding_vector,
1297 - 'metadata' => array(
1298 - 'text' => $content,
1299 - 'source_url' => $url,
1300 - 'type' => $is_product ? 'product' : 'content',
1301 - 'last_updated' => time()
1302 - )
1303 - )
1304 - )
1305 - );
1407 + for ($i = $start_page; $i <= $end_page; $i++) {
1408 + $class = $i === $page ? 'mxchat-pagination-button active' : 'mxchat-pagination-button';
1409 + echo '<button class="' . $class . '" data-page="' . $i . '">' . $i . '</button>';
1410 + }
1306 1411
1307 - $response = wp_remote_post($api_endpoint, array(
1308 - 'headers' => array(
1309 - 'Api-Key' => $api_key,
1310 - 'accept' => 'application/json',
1311 - 'content-type' => 'application/json'
1312 - ),
1313 - 'body' => wp_json_encode($request_body),
1314 - 'timeout' => 30,
1315 - 'data_format' => 'body'
1316 - ));
1412 + if ($end_page < $total_pages) {
1413 + if ($end_page < $total_pages - 1) {
1414 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1415 + }
1416 + echo '<button class="mxchat-pagination-button" data-page="' . $total_pages . '">' . $total_pages . '</button>';
1417 + }
1317 1418
1318 - if (is_wp_error($response)) {
1319 - return array(
1320 - 'success' => false,
1321 - 'message' => $response->get_error_message()
1322 - );
1419 + if ($page < $total_pages) {
1420 + echo '<button class="mxchat-pagination-button" data-page="' . ($page + 1) . '">' . esc_html__('Next', 'mxchat') . ' &raquo;</button>';
1421 + }
1422 +
1423 + echo '</div>';
1323 1424 }
1324 1425
1325 - $response_code = wp_remote_retrieve_response_code($response);
1326 - if ($response_code !== 200) {
1327 - $body = wp_remote_retrieve_body($response);
1328 - return array(
1329 - 'success' => false,
1330 - 'message' => sprintf(
1331 - 'Pinecone API error (HTTP %d): %s',
1332 - $response_code,
1333 - $body
1334 - )
1335 - );
1336 - }
1426 + echo '</div>';
1337 1427
1338 - return array(
1339 - 'success' => true,
1340 - 'message' => 'Successfully stored in Pinecone'
1341 - );
1428 + wp_send_json([
1429 + 'html' => ob_get_clean(),
1430 + 'page' => $page,
1431 + 'total_pages' => $total_pages,
1432 + 'total_sessions' => $total_sessions
1433 + ]);
1434 +
1435 + wp_die();
1342 1436 }
1343 -// Helper method for WordPress DB storage
1344 -private function store_in_wordpress_db($content, $url, $embedding_vector) {
1345 - global $wpdb;
1346 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1347 1437
1348 - $existing_entry = $wpdb->get_row(
1349 - $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url)
1350 - );
1351 1438
1352 - if ($existing_entry) {
1353 - $wpdb->update(
1354 - $table_name,
1355 - array(
1356 - 'article_content' => $content,
1357 - 'embedding_vector' => serialize($embedding_vector),
1358 - 'timestamp' => current_time('mysql')
1359 - ),
1360 - array('id' => $existing_entry->id),
1361 - array('%s', '%s', '%s')
1439 +/**
1440 + * ALTERNATIVE: Simpler helper method using string replacement
1441 + */
1442 +private function highlight_clicked_links($message_content, $clicked_urls) {
1443 + if (empty($clicked_urls)) {
1444 + return wp_kses(
1445 + $message_content,
1446 + [
1447 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1448 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1449 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1450 + ]
1362 1451 );
1363 - } else {
1364 - $wpdb->insert(
1365 - $table_name,
1366 - array(
1367 - 'article_content' => $content,
1368 - 'source_url' => $url,
1369 - 'embedding_vector' => serialize($embedding_vector),
1370 - 'timestamp' => current_time('mysql')
1371 - ),
1372 - array('%s', '%s', '%s', '%s')
1373 - );
1374 1452 }
1375 -}
1376 -/**
1377 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1378 - *
1379 - * @param int $post_id The ID of the post being deleted
1380 - * @return void
1381 - */
1382 -public function mxchat_handle_post_delete($post_id) {
1383 - // Get post data before it's deleted
1384 - $post = get_post($post_id);
1385 1453
1386 - // Basic validation
1387 - if (!$post || wp_is_post_revision($post_id)) {
1388 - return;
1389 - }
1454 + // First apply standard sanitization
1455 + $message_content = wp_kses(
1456 + $message_content,
1457 + [
1458 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1459 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1460 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1461 + ]
1462 + );
1390 1463
1391 - // Check post type
1392 - $post_type = $post->post_type;
1393 - if (!in_array($post_type, array('post', 'page'))) {
1394 - return;
1395 - }
1464 + // Process each clicked URL
1465 + foreach ($clicked_urls as $clicked_url) {
1466 + // Try multiple patterns to catch different link formats
1467 + $patterns = [
1468 + // Standard link format
1469 + '/<a([^>]*href=["\']' . preg_quote($clicked_url, '/') . '["\'][^>]*)>/i',
1470 + // Link with trailing slash
1471 + '/<a([^>]*href=["\']' . preg_quote(rtrim($clicked_url, '/'), '/') . '\/?["\'][^>]*)>/i',
1472 + // Encoded entities version
1473 + '/<a([^>]*href=["\']' . preg_quote(htmlentities($clicked_url), '/') . '["\'][^>]*)>/i',
1474 + ];
1396 1475
1397 - // Check if sync is enabled for this post type
1398 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1399 - if (get_option($sync_option) != '1') {
1400 - return;
1401 - }
1476 + foreach ($patterns as $pattern) {
1477 + if (preg_match($pattern, $message_content)) {
1478 + $message_content = preg_replace_callback(
1479 + $pattern,
1480 + function($matches) {
1481 + $full_match = $matches[0];
1482 + $attributes = $matches[1];
1402 1483
1403 - // Get the URL before post is deleted
1404 - $source_url = get_permalink($post_id);
1405 - if (!$source_url) {
1406 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1407 - return;
1408 - }
1484 + // Check if it already has the class
1485 + if (strpos($full_match, 'mxchat-clicked-link') !== false) {
1486 + return $full_match;
1487 + }
1409 1488
1410 - // Check for Pinecone addon and its settings
1411 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1412 - $use_pinecone = false;
1413 - if ($pinecone_settings && is_array($pinecone_settings)) {
1414 - $use_pinecone = (
1415 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1416 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1417 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1418 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1419 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1420 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1421 - );
1422 - }
1489 + // Check if class attribute exists
1490 + if (preg_match('/class=["\']([^"\']*)["\']/', $attributes, $class_matches)) {
1491 + // Add to existing class
1492 + $new_attributes = preg_replace(
1493 + '/class=["\']([^"\']*)["\']/',
1494 + 'class="$1 mxchat-clicked-link"',
1495 + $attributes
1496 + );
1497 + } else {
1498 + // Add new class attribute
1499 + $new_attributes = $attributes . ' class="mxchat-clicked-link"';
1500 + }
1423 1501
1424 - $deletion_successful = false;
1502 + // Add title if not present
1503 + if (strpos($new_attributes, 'title=') === false) {
1504 + $new_attributes .= ' title="User clicked this link"';
1505 + }
1425 1506
1426 - if ($use_pinecone) {
1427 - try {
1428 - $pinecone_result = $this->delete_from_pinecone(
1429 - array($source_url),
1430 - $pinecone_settings['mxchat_pinecone_api_key'],
1431 - $pinecone_settings['mxchat_pinecone_environment'],
1432 - $pinecone_settings['mxchat_pinecone_index']
1433 - );
1507 + return '<a' . $new_attributes . '>';
1508 + },
1509 + $message_content
1510 + );
1434 1511
1435 - if (!$pinecone_result['success']) {
1436 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1437 - } else {
1438 - $deletion_successful = true;
1512 + // If we found and replaced, break out of the patterns loop
1513 + break;
1439 1514 }
1440 - } catch (Exception $e) {
1441 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1442 1515 }
1443 1516 }
1444 1517
1445 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1446 - try {
1447 - global $wpdb;
1448 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1449 -
1450 - $result = $wpdb->delete(
1451 - $table_name,
1452 - array('source_url' => $source_url),
1453 - array('%s')
1454 - );
1455 -
1456 - if ($result === false) {
1457 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1458 - } else {
1459 - $deletion_successful = true;
1460 - }
1461 - } catch (Exception $e) {
1462 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1463 - }
1464 -
1465 - if (!$deletion_successful) {
1466 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1467 - }
1518 + return $message_content;
1468 1519 }
1469 1520
1521 +public function mxchat_create_prompts_page() {
1522 + //error_log('=== DEBUG: mxchat_create_prompts_page started ===');
1470 1523
1471 -public function mxchat_handle_content_submission() {
1472 - // Check if the form was submitted and the user has permission.
1473 - if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1474 - return;
1475 - }
1524 + global $wpdb;
1525 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1476 1526
1477 - // Verify the nonce.
1478 - $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1479 - if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1480 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1481 - }
1527 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1528 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
1482 1529
1483 - // Sanitize the inputs.
1484 - $article_content = sanitize_textarea_field($_POST['article_content']);
1485 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1486 -
1487 - // Generate the embedding vector.
1488 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1489 - if (!is_array($embedding_vector)) {
1490 - set_transient('mxchat_admin_notice_error',
1491 - esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1492 - 30
1493 - );
1494 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1495 - exit;
1530 + // Display success message if all prompts were deleted
1531 + if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
1532 + echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
1496 1533 }
1497 1534
1498 - // Store in the WordPress database.
1499 - global $wpdb;
1500 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1535 + // Set up pagination and search query
1536 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
1537 + $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
1538 + $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
1539 + $per_page = 10;
1501 1540
1502 - // Ensure the source_url column exists.
1503 - if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1504 - $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1505 - }
1541 + //error_log('DEBUG: Search query: ' . $search_query);
1542 + //error_log('DEBUG: Current page: ' . $current_page);
1543 + //error_log('DEBUG: Per page: ' . $per_page);
1506 1544
1507 - // Serialize and store the embedding vector.
1508 - $embedding_vector_serialized = serialize($embedding_vector);
1509 - $inserted = $wpdb->insert(
1510 - $table_name,
1511 - array(
1512 - 'article_content' => $article_content,
1513 - 'embedding_vector' => $embedding_vector_serialized,
1514 - 'source_url' => $article_url,
1515 - ),
1516 - array('%s', '%s', '%s')
1517 - );
1518 -
1519 - if ($inserted === false) {
1520 - set_transient('mxchat_admin_notice_error',
1521 - esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1522 - 30
1523 - );
1545 + // ================================
1546 + // MULTI-BOT CONFIGURATION
1547 + // ================================
1548 +
1549 + // Check for multi-bot and set up bot selection
1550 + if (class_exists('MxChat_Multi_Bot_Manager')) {
1551 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
1552 + $available_bots = $multi_bot_manager->get_available_bots();
1553 +
1554 + // Get saved bot selection (user-specific first, then site-wide default)
1555 + $user_id = get_current_user_id();
1556 + $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
1557 + if (empty($saved_bot_id)) {
1558 + $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
1559 + }
1560 +
1561 + // Allow URL override but default to saved selection
1562 + $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
1563 + $multibot_active = true;
1524 1564 } else {
1525 - set_transient('mxchat_admin_notice_success',
1526 - esc_html__('Content successfully submitted!', 'mxchat'),
1527 - 30
1528 - );
1565 + $current_bot_id = 'default';
1566 + $multibot_active = false;
1529 1567 }
1530 1568
1531 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1532 - exit;
1533 -}
1569 + // ================================
1570 + // DATA SOURCE CONFIGURATION
1571 + // ================================
1534 1572
1535 -private function is_pdf_url($url, $response) {
1536 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1537 - $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1573 + // Get bot-specific Pinecone settings
1574 + $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($current_bot_id);
1575 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
1576 + $pinecone_api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
1538 1577
1539 - return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1540 -}
1541 -private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1542 - if (!current_user_can('manage_options')) {
1543 - //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat'));
1544 - return false;
1545 - }
1578 + //error_log('DEBUG: Bot ' . $current_bot_id . ' - Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
1579 + //error_log('DEBUG: Bot ' . $current_bot_id . ' - Has API Key: ' . (!empty($pinecone_api_key) ? 'YES' : 'NO'));
1580 + //error_log('DEBUG: Bot ' . $current_bot_id . ' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
1581 + //error_log('DEBUG: Bot ' . $current_bot_id . ' - Namespace: ' . ($pinecone_options['mxchat_pinecone_namespace'] ?? 'NOT SET'));
1546 1582
1547 - $pdf_url = esc_url_raw($pdf_url);
1548 - $upload_dir = wp_upload_dir();
1583 + if ($use_pinecone && !empty($pinecone_api_key)) {
1584 + //error_log('DEBUG: Using PINECONE data source with bot-specific config');
1585 + // PINECONE DATA SOURCE
1586 + $data_source = 'pinecone';
1587 +
1588 + // IMPORTANT: Pass the bot-specific $pinecone_options, not default options!
1589 + $records = $pinecone_manager->mxchat_fetch_pinecone_records($pinecone_options, $search_query, $current_page, $per_page, $current_bot_id);
1549 1590
1550 - if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1551 - //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error'])));
1552 - return false;
1553 - }
1591 + // TEMPORARY DEBUG - Add this right after the fetch call
1592 + //error_log('=== DEBUG: Bot switching issue ===');
1593 + //error_log('Current bot ID: ' . $current_bot_id);
1594 + //error_log('Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
1595 + //error_log('Records returned: ' . count($records['data'] ?? []));
1596 + //error_log('Total records: ' . ($records['total'] ?? 0));
1554 1597
1555 - $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1556 - $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1598 + // Check first few records to see their bot_id
1599 + if (!empty($records['data'])) {
1600 + foreach (array_slice($records['data'], 0, 3) as $i => $record) {
1601 + $record_bot_id = $record->bot_id ?? 'NOT_SET';
1602 + //error_log('Record ' . ($i+1) . ' bot_id: ' . $record_bot_id . ', content preview: ' . substr($record->article_content ?? '', 0, 30) . '...');
1603 + }
1604 + }
1605 + //error_log('=== END DEBUG ===');
1557 1606
1558 - $response_body = wp_remote_retrieve_body($response);
1559 - if (empty($response_body)) {
1560 - //error_log(esc_html__('Empty PDF response body', 'mxchat'));
1561 - return false;
1562 - }
1607 + $total_records = $records['total'] ?? 0;
1608 + $prompts = $records['data'] ?? array();
1609 + $total_in_database = $records['total_in_database'] ?? 0;
1610 + $showing_recent_only = $records['showing_recent_only'] ?? false;
1563 1611
1564 - if (!wp_mkdir_p(dirname($pdf_path))) {
1565 - //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path)));
1566 - return false;
1567 - }
1612 + $total_pages = ceil($total_records / $per_page);
1568 1613
1569 - try {
1570 - file_put_contents($pdf_path, $response_body);
1614 + } else {
1615 + //error_log('DEBUG: Using WORDPRESS DB data source');
1616 + // WORDPRESS DB DATA SOURCE (your existing logic)
1617 + $data_source = 'wordpress';
1571 1618
1572 - if (!file_exists($pdf_path)) {
1573 - throw new Exception(__('Failed to save PDF file', 'mxchat'));
1574 - }
1619 + // Initialize these variables for WordPress DB
1620 + $total_in_database = 0;
1621 + $showing_recent_only = false;
1575 1622
1576 - $parser = new \Smalot\PdfParser\Parser();
1577 - $pdf = $parser->parseFile($pdf_path);
1578 - $total_pages = absint(count($pdf->getPages()));
1623 + $offset = ($current_page - 1) * $per_page;
1579 1624
1580 - if ($total_pages < 1) {
1581 - throw new Exception(__('Invalid PDF: no pages found', 'mxchat'));
1625 + // Modify query to handle search input
1626 + $sql_search = "";
1627 + if ($search_query) {
1628 + $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
1582 1629 }
1583 1630
1584 - wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1585 - 'pdf_path' => $pdf_path,
1586 - 'pdf_url' => $pdf_url,
1587 - 'total_pages' => $total_pages,
1588 - 'batch_size' => absint(15),
1589 - 'batch_pause' => absint(10)
1590 - ));
1631 + // Retrieve total number of prompts, considering search filter
1632 + $count_query = "SELECT COUNT(*) FROM {$table_name} {$sql_search}";
1633 + $total_records = $wpdb->get_var($count_query);
1634 + $total_pages = ceil($total_records / $per_page);
1591 1635
1592 - $status_data = array(
1593 - 'total_pages' => $total_pages,
1594 - 'processed_pages' => 0,
1595 - 'status' => 'processing',
1596 - 'last_update' => time()
1636 + // Retrieve prompts from the database
1637 + $prompts_query = $wpdb->prepare(
1638 + "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
1639 + $per_page,
1640 + $offset
1597 1641 );
1598 1642
1599 - set_transient(
1600 - sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1601 - array_map('sanitize_text_field', $status_data),
1602 - DAY_IN_SECONDS
1603 - );
1643 + $prompts = $wpdb->get_results($prompts_query);
1644 + }
1604 1645
1605 - return __('scheduled', 'mxchat');
1606 -
1607 - } catch (Exception $e) {
1608 - //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1609 - if (file_exists($pdf_path)) {
1610 - wp_delete_file($pdf_path);
1646 + // ================================
1647 + // PAGINATION GENERATION
1648 + // ================================
1649 +
1650 + // Generate pagination links
1651 + if ($total_pages > 1) {
1652 + // Build base URL with necessary parameters
1653 + $base_url = add_query_arg('paged', '%#%');
1654 +
1655 + // Preserve bot_id parameter if multi-bot is active
1656 + if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') {
1657 + $base_url = add_query_arg('bot_id', $current_bot_id, $base_url);
1611 1658 }
1612 - return false;
1659 +
1660 + // Preserve search query if present
1661 + if ($search_query) {
1662 + $base_url = add_query_arg('search', $search_query, $base_url);
1663 + }
1664 +
1665 + $page_links = paginate_links(array(
1666 + 'base' => $base_url,
1667 + 'format' => '',
1668 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
1669 + 'next_text' => __('Next &raquo;', 'mxchat'),
1670 + 'total' => $total_pages,
1671 + 'current' => $current_page,
1672 + 'type' => 'plain',
1673 + ));
1674 + } else {
1675 + $page_links = '';
1613 1676 }
1614 -}
1615 -public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1616 - // Validate inputs
1617 - $pdf_path = sanitize_text_field($pdf_path);
1618 - $pdf_url = esc_url_raw($pdf_url);
1619 - $total_pages = absint($total_pages);
1620 - $batch_size = absint($batch_size);
1621 - $batch_pause = absint($batch_pause);
1622 1677
1623 - try {
1624 - if (!file_exists($pdf_path)) {
1625 - throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1626 - }
1678 + // ================================
1679 + // PROCESSING STATUS RETRIEVAL
1680 + // ================================
1681 +
1682 + // Retrieve processing statuses
1683 + $pdf_url = get_transient('mxchat_last_pdf_url');
1684 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
1685 + $pdf_status = $pdf_url ? $knowledge_manager->mxchat_get_pdf_processing_status($pdf_url) : false;
1686 + $sitemap_status = $sitemap_url ? $knowledge_manager->mxchat_get_sitemap_processing_status($sitemap_url) : false;
1627 1687
1628 - $parser = new \Smalot\PdfParser\Parser();
1629 - $pdf = $parser->parseFile($pdf_path);
1630 - $pages = $pdf->getPages();
1688 + $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
1689 + || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
1631 1690
1632 - // Get current progress
1633 - $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1634 - $status = get_transient($status_key);
1691 + //error_log('=== DEBUG: mxchat_create_prompts_page data preparation completed ===');
1635 1692
1636 - if (!$status || !is_array($status)) {
1637 - throw new Exception('Invalid status data retrieved from transient');
1638 - }
1693 + ?>
1639 1694
1640 - $start_page = absint($status['processed_pages']);
1641 - $end_page = min($start_page + $batch_size, $total_pages);
1695 + <div class="wrap mxchat-wrapper">
1696 + <!-- Hero Section -->
1697 + <div class="mxchat-hero">
1698 + <h1 class="mxchat-main-title">
1699 + <span class="mxchat-gradient-text">Knowledge Base</span> Manager
1700 + </h1>
1701 + <p class="mxchat-hero-subtitle">
1702 + <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
1703 + </p>
1704 + </div>
1642 1705
1643 - $instance = new self(); // Create an instance of the class
1706 + <div class="mxchat-content">
1707 +
1708 + <!-- Multi-Bot Selector (only shows if multi-bot addon is active) -->
1709 + <?php if (class_exists('MxChat_Multi_Bot_Manager')) :
1710 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
1711 + $available_bots = $multi_bot_manager->get_available_bots();
1712 +
1713 + // Get saved bot selection (user-specific first, then site-wide default)
1714 + $user_id = get_current_user_id();
1715 + $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
1716 + if (empty($saved_bot_id)) {
1717 + $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
1718 + }
1719 +
1720 + // Allow URL override but default to saved selection
1721 + $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
1722 + ?>
1723 + <div class="mxchat-card" style="margin-bottom: 20px;">
1724 + <div class="mxchat-bot-selector-wrapper" style="display: flex; align-items: center; gap: 15px;">
1725 + <label for="mxchat-bot-selector" style="font-weight: 600;">
1726 + <?php esc_html_e('Select Bot Database:', 'mxchat'); ?>
1727 + </label>
1728 + <select id="mxchat-bot-selector" class="mxchat-bot-selector" style="min-width: 200px;">
1729 + <?php foreach ($available_bots as $bot_id => $bot_name) : ?>
1730 + <option value="<?php echo esc_attr($bot_id); ?>" <?php selected($current_bot_id, $bot_id); ?>>
1731 + <?php echo esc_html($bot_name); ?>
1732 + </option>
1733 + <?php endforeach; ?>
1734 + </select>
1735 + <span class="mxchat-bot-info" style="color: #666; font-size: 13px;">
1736 + <?php esc_html_e('Content will be added to the selected bot\'s knowledge base', 'mxchat'); ?>
1737 + </span>
1738 + <span id="mxchat-bot-save-status" style="display: none; color: #00a32a; font-size: 13px;">
1739 + ✓ <?php esc_html_e('Saved', 'mxchat'); ?>
1740 + </span>
1741 + </div>
1742 + </div>
1644 1743
1645 - for ($i = $start_page; $i < $end_page; $i++) {
1646 - $text = $pages[$i]->getText();
1647 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1744 + <?php
1745 + // Set a flag so we know multi-bot is active throughout the page
1746 + $multibot_active = true;
1747 + else:
1748 + $current_bot_id = 'default';
1749 + $multibot_active = false;
1750 + endif;
1751 + ?>
1648 1752
1649 - if (!empty($sanitized_content)) {
1650 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1651 - if (is_array($embedding_vector)) {
1652 - $metadata = array(
1653 - 'document_type' => 'pdf',
1654 - 'total_pages' => $total_pages,
1655 - 'current_page' => $i + 1,
1656 - 'prev_page' => $i > 0 ? $i : null,
1657 - 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1658 - 'source_url' => $pdf_url
1659 - );
1753 + <!-- Tab Navigation -->
1754 + <div class="mxchat-kb-tabs-nav">
1755 + <button class="mxchat-kb-tab-button active" data-tab="import">
1756 + <?php esc_html_e('Knowledge Import', 'mxchat'); ?>
1757 + </button>
1758 + <button class="mxchat-kb-tab-button" data-tab="sync">
1759 + <?php esc_html_e('Settings', 'mxchat'); ?>
1760 + </button>
1761 + <button class="mxchat-kb-tab-button" data-tab="pinecone">
1762 + <?php esc_html_e('Pinecone Settings', 'mxchat'); ?>
1763 + </button>
1764 + </div>
1660 1765
1661 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1662 - $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1766 + <div class="mxchat-kb-tabs-content">
1767 + <div id="mxchat-kb-tab-import" class="mxchat-kb-tab-content active">
1768 + <!-- Import Options Card -->
1769 + <div class="mxchat-card">
1663 1770
1664 - $options = get_option('mxchat_options');
1665 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1666 - }
1667 - }
1771 + <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
1668 1772
1669 - // Update progress with sanitized data
1670 - $status['processed_pages'] = absint($i + 1);
1671 - $status['last_update'] = time();
1672 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1673 - }
1773 + <?php
1774 + // Check if the appropriate embedding API key exists
1775 + $embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
1776 + $has_openai_key = !empty($this->options['api_key']);
1777 + $has_voyage_key = !empty($this->options['voyage_api_key']);
1778 + $has_gemini_key = !empty($this->options['gemini_api_key']);
1674 1779
1675 - // Schedule next batch if needed
1676 - if ($end_page < $total_pages) {
1677 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
1678 - 'pdf_path' => $pdf_path,
1679 - 'pdf_url' => $pdf_url,
1680 - 'total_pages' => $total_pages,
1681 - 'batch_size' => $batch_size,
1682 - 'batch_pause' => $batch_pause
1683 - ));
1684 - } else {
1685 - // Processing complete
1686 - $status['status'] = 'complete';
1687 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1688 - if (file_exists($pdf_path)) {
1689 - wp_delete_file($pdf_path);
1690 - }
1691 - }
1780 + // Determine if they have the needed API key for their selected embedding model
1781 + $has_required_key = false;
1782 + $required_key_type = '';
1692 1783
1693 - } catch (\Exception $e) {
1694 - $status['status'] = 'error';
1695 - $status['error'] = sanitize_text_field($e->getMessage());
1696 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1697 - if (file_exists($pdf_path)) {
1698 - wp_delete_file($pdf_path);
1699 - }
1700 - }
1701 -}
1702 -public function get_pdf_processing_status($pdf_url) {
1703 - $pdf_url = esc_url_raw($pdf_url);
1704 - $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1784 + if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
1785 + $has_required_key = true;
1786 + $required_key_type = 'OpenAI';
1787 + } elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
1788 + $has_required_key = true;
1789 + $required_key_type = 'Voyage AI';
1790 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false && $has_gemini_key) {
1791 + $has_required_key = true;
1792 + $required_key_type = 'Google Gemini';
1793 + } elseif (strpos($embedding_model, 'text-embedding-') !== false) {
1794 + $required_key_type = 'OpenAI';
1795 + } elseif (strpos($embedding_model, 'voyage-') !== false) {
1796 + $required_key_type = 'Voyage AI';
1797 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false) {
1798 + $required_key_type = 'Google Gemini';
1799 + }
1800 + ?>
1705 1801
1706 - if (!$status || !is_array($status)) {
1707 - return false;
1708 - }
1802 + <div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
1803 + <?php if ($has_required_key): ?>
1804 + <p><span class="dashicons dashicons-yes-alt"></span> <?php echo wp_kses_post(sprintf(__('We detected your %s API key. <strong>Remember to add credits to your %s account</strong> before using the knowledgebase.', 'mxchat'), $required_key_type, $required_key_type)); ?></p>
1805 + <?php else: ?>
1806 + <p><span class="dashicons dashicons-warning"></span> <strong><?php esc_html_e('Important:', 'mxchat'); ?></strong> <?php echo sprintf(esc_html__('Before importing knowledge, you must add a %s API key with sufficient credits in the Chatbot settings.', 'mxchat'), $required_key_type); ?> <a href="<?php echo admin_url('admin.php?page=mxchat-max'); ?>"><?php esc_html_e('Go to API Key Settings', 'mxchat'); ?></a></p>
1807 + <?php endif; ?>
1808 + </div>
1709 1809
1710 - return array(
1711 - 'total_pages' => absint($status['total_pages']),
1712 - 'processed_pages' => absint($status['processed_pages']),
1713 - 'percentage' => ($status['total_pages'] > 0)
1714 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1715 - : 0,
1716 - 'status' => sanitize_text_field($status['status']),
1717 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1718 - );
1719 -}
1720 -public function mxchat_handle_sitemap_submission() {
1721 - // Check if the form was submitted and verify permissions
1722 - if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
1723 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
1724 - }
1810 + <!-- Import Options Section -->
1811 + <div class="mxchat-import-section">
1812 + <h3><?php esc_html_e('Import Options', 'mxchat'); ?></h3>
1725 1813
1726 - // Verify nonce
1727 - check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1814 + <!-- Import Options Grid -->
1815 + <div class="mxchat-import-options">
1816 + <!-- WordPress Import Option -->
1817 + <button type="button" id="mxchat-open-content-selector" class="mxchat-import-box mxchat-import-wordpress" data-option="wordpress">
1818 + <div class="mxchat-import-icon">
1819 + <span class="dashicons dashicons-wordpress"></span>
1820 + </div>
1821 + <div class="mxchat-import-content">
1822 + <h4><?php esc_html_e('WordPress Content', 'mxchat'); ?></h4>
1823 + <p><?php esc_html_e('Import specific posts and pages to your knowledge base.', 'mxchat'); ?></p>
1824 + </div>
1825 + <div class="mxchat-recommended-tag"><?php esc_html_e('Recommended', 'mxchat'); ?></div>
1826 + </button>
1728 1827
1729 - // Validate URL
1730 - if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
1731 - set_transient('mxchat_admin_notice_error',
1732 - esc_html__('Please provide a valid URL.', 'mxchat'),
1733 - 30
1734 - );
1735 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1736 - exit;
1737 - }
1828 + <!-- Sitemap Import Option -->
1829 + <button type="button" class="mxchat-import-box" data-option="sitemap" data-placeholder="<?php esc_attr_e('Enter sitemap URL here', 'mxchat'); ?>" data-type="sitemap">
1830 + <div class="mxchat-import-icon">
1831 + <span class="dashicons dashicons-admin-site-alt"></span>
1832 + </div>
1833 + <div class="mxchat-import-content">
1834 + <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1835 + <p><?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
1836 + </div>
1837 + </button>
1738 1838
1739 - $submitted_url = esc_url_raw($_POST['sitemap_url']);
1740 - $response = wp_remote_get($submitted_url, array('timeout' => 30));
1839 + <!-- Direct URL Import Option -->
1840 + <button type="button" class="mxchat-import-box" data-option="url" data-placeholder="<?php esc_attr_e('Enter webpage URL here', 'mxchat'); ?>" data-type="url">
1841 + <div class="mxchat-import-icon">
1842 + <span class="dashicons dashicons-admin-links"></span>
1843 + </div>
1844 + <div class="mxchat-import-content">
1845 + <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
1846 + <p><?php esc_html_e('Import content from any webpage.', 'mxchat'); ?></p>
1847 + </div>
1848 + </button>
1741 1849
1742 - if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1743 - $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat');
1744 - set_transient('mxchat_admin_notice_error',
1745 - sprintf(
1746 - esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1747 - esc_html($error_message)
1748 - ),
1749 - 30
1750 - );
1751 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1752 - exit;
1753 - }
1850 + <!-- Direct Content Import Option -->
1851 + <button type="button" class="mxchat-import-box" data-option="content" data-type="content">
1852 + <div class="mxchat-import-icon">
1853 + <span class="dashicons dashicons-editor-paste-text"></span>
1854 + </div>
1855 + <div class="mxchat-import-content">
1856 + <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
1857 + <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p>
1858 + </div>
1859 + </button>
1754 1860
1755 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1756 - $body_content = wp_remote_retrieve_body($response);
1861 + <!-- PDF Import Option -->
1862 + <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf">
1863 + <div class="mxchat-import-icon">
1864 + <span class="dashicons dashicons-media-document"></span>
1865 + </div>
1866 + <div class="mxchat-import-content">
1867 + <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1868 + <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p>
1869 + </div>
1870 + </button>
1871 + </div>
1757 1872
1758 - if (empty($body_content)) {
1759 - set_transient('mxchat_admin_notice_error',
1760 - esc_html__('Empty response received from URL.', 'mxchat'),
1761 - 30
1762 - );
1763 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1764 - exit;
1765 - }
1873 + <!-- Input Areas for URL and Content -->
1874 + <div class="mxchat-import-input-area" id="mxchat-url-input-area" style="display: none;">
1875 + <?php if (!$is_processing) : ?>
1876 + <form id="mxchat-url-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
1877 + <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
1878 + <input type="hidden" name="import_type" id="import_type" value="url">
1879 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
1880 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
1881 + <?php endif; ?>
1882 + <div class="mxchat-url-input-group">
1883 + <input type="url"
1884 + name="sitemap_url"
1885 + id="sitemap_url"
1886 + placeholder="<?php esc_attr_e('Enter URL here', 'mxchat'); ?>"
1887 + required />
1888 + <button type="submit"
1889 + name="submit_sitemap"
1890 + class="mxchat-button-primary">
1891 + <?php esc_html_e('Import', 'mxchat'); ?>
1892 + </button>
1893 + </div>
1894 + <p class="mxchat-url-description" id="url-description-text"></p>
1895 + </form>
1896 + <?php endif; ?>
1897 + </div>
1766 1898
1767 - // Handle PDF URL
1768 - if ($this->is_pdf_url($submitted_url, $response)) {
1769 - $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
1899 + <div class="mxchat-import-input-area" id="mxchat-content-input-area" style="display: none;">
1900 + <?php if (!$is_processing) : ?>
1901 + <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
1902 + <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
1903 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
1904 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
1905 + <?php endif; ?>
1906 + <div class="mxchat-form-group">
1907 + <textarea
1908 + name="article_content"
1909 + id="article_content"
1910 + placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"
1911 + required
1912 + rows="6"
1913 + ></textarea>
1914 + </div>
1915 + <div class="mxchat-form-group">
1916 + <input type="url"
1917 + name="article_url"
1918 + id="article_url"
1919 + placeholder="<?php esc_attr_e('Enter source URL (Optional)', 'mxchat'); ?>">
1920 + <div class="mxchat-url-helper" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
1921 + <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: #666; cursor: pointer;">
1922 + <input type="checkbox" id="mxchat-unique-url-toggle" style="margin: 0;">
1923 + <?php esc_html_e('Generate unique URL for duplicate content', 'mxchat'); ?>
1924 + </label>
1925 + <button type="button"
1926 + id="mxchat-generate-unique-url"
1927 + class="mxchat-button-secondary"
1928 + style="padding: 4px 12px; font-size: 12px; display: none;">
1929 + <span class="dashicons dashicons-randomize" style="font-size: 14px; margin-top: 2px;"></span>
1930 + <?php esc_html_e('Generate Unique', 'mxchat'); ?>
1931 + </button>
1932 + </div>
1933 + <p class="description" style="margin-top: 8px; font-size: 12px; color: #666;">
1934 + <?php esc_html_e('Enable this option to submit multiple entries with the same base URL. A unique reference will be appended (e.g., ?ref=1731234567890).', 'mxchat'); ?>
1935 + </p>
1936 + </div>
1937 + <button type="submit"
1938 + name="submit_content"
1939 + class="mxchat-button-primary">
1940 + <?php esc_html_e('Import Content', 'mxchat'); ?>
1941 + </button>
1942 + </form>
1943 + <?php endif; ?>
1944 + </div>
1945 + </div>
1770 1946
1771 - if ($result === 'scheduled') {
1772 - set_transient(
1773 - 'mxchat_last_pdf_url',
1774 - sanitize_text_field($submitted_url),
1775 - DAY_IN_SECONDS
1776 - );
1777 - set_transient('mxchat_admin_notice_info',
1778 - esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1779 - 30
1780 - );
1781 - } else {
1782 - set_transient('mxchat_admin_notice_error',
1783 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
1784 - 30
1785 - );
1786 - }
1947 + <!-- PDF Processing Status - ONLY PROCESSING -->
1948 + <?php if ($pdf_status && $pdf_status['status'] === 'processing') : ?>
1949 + <div class="mxchat-status-card" data-card-type="pdf">
1950 + <div class="mxchat-status-header">
1951 + <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1787 1952
1788 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1789 - exit;
1790 - }
1953 + <!-- Processing controls -->
1954 + <div class="mxchat-processing-controls">
1955 + <form method="post" class="mxchat-stop-form"
1956 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1957 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1958 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1959 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1960 + </button>
1961 + </form>
1791 1962
1792 - // Handle Sitemap XML
1793 - if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
1794 - libxml_use_internal_errors(true);
1795 - $xml = simplexml_load_string($body_content);
1796 - $xml_errors = libxml_get_errors();
1797 - libxml_clear_errors();
1963 + <button type="button" class="mxchat-manual-batch-btn"
1964 + data-process-type="pdf"
1965 + data-url="<?php echo esc_attr(get_transient('mxchat_last_pdf_url')); ?>">
1966 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
1967 + </button>
1968 + </div>
1969 + </div>
1798 1970
1799 - if ($xml === false || !empty($xml_errors)) {
1800 - set_transient('mxchat_admin_notice_error',
1801 - esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1802 - 30
1803 - );
1804 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1805 - exit;
1806 - }
1971 + <div class="mxchat-progress-bar">
1972 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1973 + </div>
1807 1974
1808 - $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
1975 + <div class="mxchat-status-details">
1976 + <p><?php printf(
1977 + esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1978 + absint($pdf_status['processed_pages']),
1979 + absint($pdf_status['total_pages']),
1980 + absint($pdf_status['percentage'])
1981 + ); ?></p>
1809 1982
1810 - if ($result === 'scheduled') {
1811 - set_transient(
1812 - 'mxchat_last_sitemap_url',
1813 - sanitize_text_field($submitted_url),
1814 - DAY_IN_SECONDS
1815 - );
1816 - set_transient('mxchat_admin_notice_info',
1817 - esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1818 - 30
1819 - );
1820 - } else {
1821 - set_transient('mxchat_admin_notice_error',
1822 - esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
1823 - 30
1824 - );
1825 - }
1983 + <?php if (!empty($pdf_status['failed_pages']) && $pdf_status['failed_pages'] > 0) : ?>
1984 + <p><strong><?php esc_html_e('Failed pages:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['failed_pages']); ?></p>
1985 + <?php endif; ?>
1826 1986
1827 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1828 - exit;
1829 - }
1987 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($pdf_status['status'])); ?></p>
1988 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['last_update']); ?></p>
1989 + </div>
1990 + </div>
1991 + <?php endif; ?>
1830 1992
1831 - // Handle Regular URL
1832 - $page_content = $this->mxchat_extract_main_content($body_content);
1833 - $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
1993 + <!-- Sitemap Processing Status - ONLY PROCESSING -->
1994 + <?php if ($sitemap_status && $sitemap_status['status'] === 'processing') : ?>
1995 + <div class="mxchat-status-card" data-card-type="sitemap">
1996 + <div class="mxchat-status-header">
1997 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
1834 1998
1835 - if (empty($sanitized_content)) {
1836 - set_transient('mxchat_admin_notice_error',
1837 - esc_html__('No valid content found on the provided URL.', 'mxchat'),
1838 - 30
1839 - );
1840 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1841 - exit;
1842 - }
1999 + <!-- Processing controls -->
2000 + <div class="mxchat-processing-controls">
2001 + <form method="post" class="mxchat-stop-form"
2002 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2003 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2004 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2005 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2006 + </button>
2007 + </form>
1843 2008
1844 - $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
1845 - if (is_array($embedding_vector)) {
1846 - MxChat_Utils::submit_content_to_db(
1847 - $sanitized_content,
1848 - $submitted_url,
1849 - $this->options['api_key']
1850 - );
1851 - set_transient('mxchat_admin_notice_success',
1852 - esc_html__('URL content successfully submitted!', 'mxchat'),
1853 - 30
1854 - );
1855 - } else {
1856 - set_transient('mxchat_admin_notice_error',
1857 - esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
1858 - 30
1859 - );
1860 - }
2009 + <button type="button" class="mxchat-manual-batch-btn"
2010 + data-process-type="sitemap"
2011 + data-url="<?php echo esc_attr(get_transient('mxchat_last_sitemap_url')); ?>">
2012 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
2013 + </button>
2014 + </div>
2015 + </div>
1861 2016
1862 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1863 - exit;
1864 -}
1865 -private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
1866 - if (!current_user_can('manage_options')) {
1867 - //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
1868 - return false;
1869 - }
2017 + <div class="mxchat-progress-bar">
2018 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
2019 + </div>
1870 2020
1871 - try {
1872 - $sitemap_url = esc_url_raw($sitemap_url);
2021 + <div class="mxchat-status-details">
2022 + <p><?php printf(
2023 + esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
2024 + absint($sitemap_status['processed_urls']),
2025 + absint($sitemap_status['total_urls']),
2026 + absint($sitemap_status['percentage'])
2027 + ); ?></p>
1873 2028
1874 - if (!$xml || !is_object($xml)) {
1875 - throw new Exception(__('Invalid XML object provided', 'mxchat'));
1876 - }
2029 + <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
2030 + <p><strong><?php esc_html_e('Failed URLs:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['failed_urls']); ?></p>
2031 + <?php endif; ?>
1877 2032
1878 - $urls = [];
1879 - foreach ($xml->url as $url_element) {
1880 - $url = esc_url_raw((string)$url_element->loc);
1881 - if ($url) {
1882 - $urls[] = $url;
1883 - }
1884 - }
2033 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($sitemap_status['status'])); ?></p>
2034 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['last_update']); ?></p>
1885 2035
1886 - $total_urls = absint(count($urls));
2036 + <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
2037 + <div class="mxchat-error-notice">
2038 + <?php if (!empty($sitemap_status['error'])) : ?>
2039 + <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
2040 + <?php endif; ?>
2041 + <?php if (!empty($sitemap_status['last_error'])) : ?>
2042 + <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
2043 + <?php endif; ?>
2044 + </div>
2045 + <?php endif; ?>
2046 + </div>
2047 + </div>
2048 + <?php endif; ?>
1887 2049
1888 - if ($total_urls < 1) {
1889 - throw new Exception(__('No valid URLs found in sitemap', 'mxchat'));
1890 - }
2050 + <!-- Single URL Submission Status -->
2051 + <?php
2052 + // Get single URL status
2053 + $single_url_status = $this->knowledge_manager->mxchat_get_single_url_status();
2054 + $is_active_processing =
2055 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
2056 + ($pdf_status && $pdf_status['status'] === 'processing');
2057 + ?>
1891 2058
1892 - wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
1893 - 'urls' => $urls,
1894 - 'sitemap_url' => $sitemap_url,
1895 - 'total_urls' => $total_urls,
1896 - 'batch_size' => absint(10),
1897 - 'batch_pause' => absint(5)
1898 - ));
2059 + <div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
2060 + <?php if ($single_url_status && !$is_active_processing) : ?>
2061 + <div class="mxchat-status-card">
2062 + <div class="mxchat-status-header">
2063 + <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
2064 + <?php if ($single_url_status['status'] === 'failed') : ?>
2065 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
2066 + <?php else : ?>
2067 + <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
2068 + <?php endif; ?>
2069 + </div>
2070 + <div class="mxchat-status-details">
2071 + <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
2072 + <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
2073 + <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
2074 + </a>
2075 + </p>
2076 + <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
1899 2077
1900 - $status_data = array(
1901 - 'total_urls' => $total_urls,
1902 - 'processed_urls' => 0,
1903 - 'status' => 'processing',
1904 - 'last_update' => time()
1905 - );
2078 + <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
2079 + <div class="mxchat-error-notice">
2080 + <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
2081 + </div>
2082 + <?php endif; ?>
1906 2083
1907 - set_transient(
1908 - sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
1909 - array_map('sanitize_text_field', $status_data),
1910 - DAY_IN_SECONDS
1911 - );
2084 + <?php if ($single_url_status['status'] === 'complete') : ?>
2085 + <p><strong><?php esc_html_e('Content Length:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['content_length']); ?> <?php esc_html_e('characters', 'mxchat'); ?></p>
2086 + <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
2087 + <?php endif; ?>
2088 + </div>
2089 + </div>
2090 + <?php endif; ?>
2091 + </div>
1912 2092
1913 - return __('scheduled', 'mxchat');
2093 + <!-- Completed Status Cards - Handles completed PDF/Sitemap processing -->
2094 + <?php
2095 + echo $knowledge_manager->mxchat_render_completed_status_cards();
2096 + ?>
1914 2097
1915 - } catch (\Exception $e) {
1916 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1917 - return false;
1918 - }
1919 -}
1920 -public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1921 - // Validate inputs
1922 - $sitemap_url = esc_url_raw($sitemap_url);
1923 - $total_urls = absint($total_urls);
1924 - $batch_size = absint($batch_size);
1925 - $batch_pause = absint($batch_pause);
2098 + </div>
1926 2099
1927 - if (!is_array($urls) || empty($urls)) {
1928 - return;
1929 - }
2100 + <!-- Knowledge Base Table Card -->
2101 + <div class="mxchat-card">
2102 + <div class="mxchat-card-header">
2103 + <h2>
2104 + <?php esc_html_e('Knowledge Base', 'mxchat'); ?>
2105 + <span class="mxchat-record-count">
2106 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2107 + (<?php echo esc_html($total_records); ?> of <?php echo esc_html(number_format($total_in_database)); ?> total - recent entries only)
2108 + <?php else: ?>
2109 + (<?php echo esc_html($total_records); ?>)
2110 + <?php endif; ?>
2111 + </span>
2112 + <!-- Data source badge -->
2113 + <?php if ($use_pinecone) : ?>
2114 + <span class="mxchat-data-source-badge pinecone" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #e3f2fd; color: #1976d2;">
2115 + <span class="dashicons dashicons-cloud"></span>
2116 + <?php esc_html_e('Pinecone', 'mxchat'); ?>
2117 + </span>
2118 + <?php else : ?>
2119 + <span class="mxchat-data-source-badge wordpress" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #f3e5f5; color: #7b1fa2;">
2120 + <span class="dashicons dashicons-database-view"></span>
2121 + <?php esc_html_e('WordPress DB', 'mxchat'); ?>
2122 + </span>
2123 + <?php endif; ?>
2124 + </h2>
2125 + <div class="mxchat-header-actions">
2126 + <!-- Search -->
2127 + <form method="get" id="knowledge-search" class="mxchat-search-form">
2128 + <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
2129 + <input type="hidden" name="page" value="mxchat-prompts" />
2130 + <?php if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') : ?>
2131 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2132 + <?php endif; ?>
2133 + <div class="mxchat-search-group">
2134 + <span class="dashicons dashicons-search"></span>
2135 + <input type="text"
2136 + name="search"
2137 + placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
2138 + value="<?php echo esc_attr($search_query); ?>" />
2139 + </div>
2140 + </form>
1930 2141
1931 - try {
1932 - $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
1933 - $status = get_transient($status_key);
2142 + <form method="post"
2143 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
2144 + class="mxchat-delete-form"
2145 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
2146 + <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
2147 + <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" />
2148 + <!-- Always include bot_id, even if it's 'default' -->
2149 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2150 + <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
2151 + <span class="dashicons dashicons-trash"></span>
2152 + <?php esc_html_e('Delete All', 'mxchat'); ?>
2153 + </button>
2154 + </form>
2155 + </div>
2156 + </div>
1934 2157
1935 - if (!$status || !is_array($status)) {
1936 - throw new Exception('Invalid status data retrieved from transient');
1937 - }
2158 + <!-- Recent Entries Info Banner -->
2159 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2160 + <div class="mxchat-info-banner recent-only" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e3f2fd; border-left: 4px solid #2196f3; color: #1565c0;">
2161 + <span class="dashicons dashicons-info"></span>
2162 + <span>
2163 + <?php printf(
2164 + esc_html__('Showing your %s most recent entries for faster loading. Your complete database contains %s entries. ', 'mxchat'),
2165 + number_format($total_records),
2166 + number_format($total_in_database)
2167 + ); ?>
2168 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('View complete database in Pinecone →', 'mxchat'); ?></a>
2169 + </span>
2170 + </div>
2171 + <?php endif; ?>
1938 2172
1939 - $start_url = absint($status['processed_urls']);
1940 - $end_url = min($start_url + $batch_size, $total_urls);
1941 - $instance = new self();
2173 + <!-- Data Source Info Banner -->
2174 + <?php if ($use_pinecone) : ?>
2175 + <div class="mxchat-info-banner pinecone" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e8f5e8; border-left: 4px solid #4caf50; color: #2e7d2e;">
2176 + <span class="dashicons dashicons-cloud"></span>
2177 + <span><?php esc_html_e('Refresh page to see new content. New entires or deletions can take up to 60 seconds to reflect! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2178 + </div>
2179 + <?php else : ?>
2180 + <div class="mxchat-info-banner wordpress" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #fff3e0; border-left: 4px solid #ff9800; color: #e65100;">
2181 + <span class="dashicons dashicons-database-view"></span>
2182 + <span><?php esc_html_e('Refresh page to see new content, but wait 5-10 seconds first! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2183 + </div>
2184 + <?php endif; ?>
1942 2185
1943 - // Track failures in batch
1944 - $batch_stats = [
1945 - 'processed' => 0,
1946 - 'failed' => 0,
1947 - 'last_error' => ''
1948 - ];
2186 + <!-- Table -->
2187 + <div class="mxchat-table-wrapper">
2188 + <table class="mxchat-records-table">
2189 + <thead>
2190 + <tr>
2191 + <th><?php esc_html_e('ID', 'mxchat'); ?></th>
2192 + <th><?php esc_html_e('Content', 'mxchat'); ?></th>
2193 + <th><?php esc_html_e('Source', 'mxchat'); ?></th>
2194 + <?php if ($data_source === 'pinecone') : ?>
2195 + <th><?php esc_html_e('Vector ID', 'mxchat'); ?></th>
2196 + <?php endif; ?>
2197 + <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2198 + </tr>
2199 + </thead>
2200 + <tbody>
2201 + <?php if ($prompts) : ?>
2202 + <?php foreach ($prompts as $index => $prompt) : ?>
2203 + <tr id="prompt-<?php echo esc_attr($prompt->id); ?>"
2204 + data-source="<?php echo esc_attr($data_source); ?>"
2205 + <?php if ($data_source === 'pinecone') : ?>
2206 + style="background: rgba(33, 150, 243, 0.02);"
2207 + <?php endif; ?>>
2208 + <td>
2209 + <?php if ($data_source === 'pinecone') : ?>
2210 + <?php echo esc_html($index + 1 + (($current_page - 1) * $per_page)); ?>
2211 + <?php else : ?>
2212 + <?php echo esc_html($prompt->id); ?>
2213 + <?php endif; ?>
2214 + </td>
2215 + <td class="mxchat-content-cell">
2216 + <div class="mxchat-accordion-wrapper">
2217 + <div class="mxchat-content-preview">
2218 + <?php
2219 + $content = $prompt->article_content;
2220 + $preview_length = 150;
2221 + $content_preview = mb_strlen($content) > $preview_length
2222 + ? mb_substr($content, 0, $preview_length) . '...'
2223 + : $content;
2224 + ?>
2225 + <span class="preview-text"><?php echo esc_html($content_preview); ?></span>
2226 + <?php if (mb_strlen($content) > $preview_length) : ?>
2227 + <button class="mxchat-expand-toggle" type="button">
2228 + <span class="dashicons dashicons-arrow-down-alt2"></span>
2229 + </button>
2230 + <?php endif; ?>
2231 + </div>
2232 + <div class="mxchat-content-full" style="display: none;">
2233 + <div class="content-view">
2234 + <?php
2235 + // Check if content contains Hebrew characters
2236 + if (preg_match('/[\x{0590}-\x{05FF}]/u', $content)) {
2237 + // Apply RTL direction for Hebrew content
2238 + echo '<div dir="rtl" lang="he" class="rtl-content">';
2239 + echo wp_kses_post(wpautop($content));
2240 + echo '</div>';
2241 + } else {
2242 + echo wp_kses_post(wpautop($content));
2243 + }
2244 + ?>
2245 + </div>
2246 + <?php if ($data_source === 'wordpress') : ?>
2247 + <textarea class="content-edit" style="display:none;"
2248 + <?php if (preg_match('/[\x{0590}-\x{05FF}]/u', $prompt->article_content)) echo 'dir="rtl" lang="he"'; ?>>
2249 + <?php echo htmlspecialchars($prompt->article_content, ENT_QUOTES, 'UTF-8'); ?>
2250 + </textarea>
2251 + <?php endif; ?>
2252 + </div>
2253 + </div>
2254 + </td>
2255 + <td class="mxchat-url-cell">
2256 + <div class="url-view">
2257 + <?php if (!empty($prompt->source_url)) : ?>
2258 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
2259 + <span class="dashicons dashicons-external"></span>
2260 + <?php esc_html_e('View Source', 'mxchat'); ?>
2261 + </a>
2262 + <?php else : ?>
2263 + <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
2264 + <?php endif; ?>
2265 + </div>
2266 + <?php if ($data_source === 'wordpress') : ?>
2267 + <input type="text" class="url-edit" style="display:none;"
2268 + value="<?php echo htmlspecialchars($prompt->source_url, ENT_QUOTES, 'UTF-8'); ?>" />
2269 + <?php endif; ?>
2270 + </td>
2271 + <?php if ($data_source === 'pinecone') : ?>
2272 + <td class="mxchat-vector-id-cell">
2273 + <code style="background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 11px;">
2274 + <?php echo esc_html(substr($prompt->id, 0, 12) . '...'); ?>
2275 + </code>
2276 + </td>
2277 + <?php endif; ?>
2278 +
2279 + <td class="mxchat-actions-cell">
2280 + <?php if ($data_source === 'wordpress') : ?>
2281 + <!-- WordPress - Full editing capabilities -->
2282 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2283 + <select class="mxchat-role-select"
2284 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2285 + data-data-source="wordpress"
2286 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2287 + <?php
2288 + $current_role = $prompt->role_restriction ?? 'public';
2289 + $role_options = $knowledge_manager->mxchat_get_role_options();
2290 + foreach ($role_options as $role_key => $role_label) : ?>
2291 + <option value="<?php echo esc_attr($role_key); ?>"
2292 + <?php selected($current_role, $role_key); ?>>
2293 + <?php echo esc_html($role_label); ?>
2294 + </option>
2295 + <?php endforeach; ?>
2296 + </select>
2297 + </div>
2298 +
2299 + <!-- Edit/Save Buttons -->
2300 + <div class="mxchat-action-buttons">
2301 + <button class="mxchat-button-icon edit-button"
2302 + data-id="<?php echo esc_attr($prompt->id); ?>">
2303 + <span class="dashicons dashicons-edit"></span>
2304 + </button>
2305 + <button class="mxchat-button-icon save-button"
2306 + data-id="<?php echo esc_attr($prompt->id); ?>"
2307 + style="display:none;"
2308 + data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
2309 + <span class="dashicons dashicons-saved"></span>
2310 + </button>
2311 + </div>
2312 + <?php else : ?>
2313 + <!-- Pinecone - Role restriction editable, content read-only -->
2314 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2315 + <select class="mxchat-role-select"
2316 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2317 + data-data-source="pinecone"
2318 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2319 + <?php
2320 + $current_role = $prompt->role_restriction ?? 'public';
2321 + $role_options = $knowledge_manager->mxchat_get_role_options();
2322 + foreach ($role_options as $role_key => $role_label) : ?>
2323 + <option value="<?php echo esc_attr($role_key); ?>"
2324 + <?php selected($current_role, $role_key); ?>>
2325 + <?php echo esc_html($role_label); ?>
2326 + </option>
2327 + <?php endforeach; ?>
2328 + </select>
2329 + </div>
2330 +
2331 + <?php endif; ?>
1949 2332
1950 - for ($i = $start_url; $i < $end_url; $i++) {
1951 - $page_url = esc_url_raw($urls[$i]);
1952 - $page_response = wp_remote_get($page_url);
2333 + <!-- Delete button for both sources -->
2334 + <div class="mxchat-delete-container">
2335 + <?php if ($data_source === 'pinecone') : ?>
2336 + <!-- AJAX Delete for Pinecone -->
2337 + <button type="button"
2338 + class="mxchat-button-icon delete-button-ajax"
2339 + data-vector-id="<?php echo esc_attr($prompt->id); ?>"
2340 + data-bot-id="<?php echo esc_attr($current_bot_id); ?>"
2341 + data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
2342 + title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
2343 + <span class="dashicons dashicons-trash"></span>
2344 + </button>
2345 + <?php else : ?>
2346 + <!-- Regular link delete for WordPress DB -->
2347 + <a href="<?php echo esc_url(admin_url(
2348 + 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
2349 + . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
2350 + )); ?>"
2351 + class="mxchat-button-icon delete-button"
2352 + onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
2353 + <span class="dashicons dashicons-trash"></span>
2354 + </a>
2355 + <?php endif; ?>
2356 + </div>
2357 + </td>
1953 2358
1954 - if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1955 - $batch_stats['failed']++;
1956 - continue;
1957 - }
2359 + </tr>
2360 + <?php endforeach; ?>
2361 + <?php else : ?>
2362 + <tr>
2363 + <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
2364 + <?php if ($use_pinecone) : ?>
2365 + <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
2366 + <?php else : ?>
2367 + <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
2368 + <?php endif; ?>
2369 + </td>
2370 + </tr>
2371 + <?php endif; ?>
2372 + </tbody>
2373 + </table>
2374 + </div>
1958 2375
1959 - $page_html = wp_remote_retrieve_body($page_response);
1960 - $page_content = $instance->mxchat_extract_main_content($page_html);
1961 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2376 + <?php if ($page_links) : ?>
2377 + <div class="mxchat-pagination">
2378 + <?php echo wp_kses_post($page_links); ?>
2379 + </div>
2380 + <?php endif; ?>
2381 + </div>
1962 2382
1963 - if (!empty($sanitized_content)) {
1964 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2383 + </div>
1965 2384
1966 - if (is_array($embedding_vector)) {
1967 - $options = get_option('mxchat_options');
1968 - $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2385 + <!-- Settings Tab (Renamed from Auto-Sync Settings) -->
2386 + <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
2387 +
2388 + <!-- Auto-Sync Settings Card -->
2389 + <div class="mxchat-card">
2390 + <div class="mxchat-settings-section">
2391 + <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
2392 + <p class="mxchat-description">
2393 + <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below. Works with Pinecone if enabled', 'mxchat'); ?>
2394 + </p>
2395 + <div class="mxchat-autosave-section">
2396 + <div class="mxchat-toggle-group">
2397 + <div class="mxchat-toggle-container">
2398 + <label class="mxchat-toggle-switch">
2399 + <input type="checkbox"
2400 + name="mxchat_auto_sync_posts"
2401 + class="mxchat-autosave-field"
2402 + value="1"
2403 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2404 + <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
2405 + <span class="mxchat-toggle-slider"></span>
2406 + </label>
2407 + <span class="mxchat-toggle-label">
2408 + <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
2409 + </span>
2410 + </div>
2411 +
2412 + <div class="mxchat-toggle-container">
2413 + <label class="mxchat-toggle-switch">
2414 + <input type="checkbox"
2415 + name="mxchat_auto_sync_pages"
2416 + class="mxchat-autosave-field"
2417 + value="1"
2418 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2419 + <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
2420 + <span class="mxchat-toggle-slider"></span>
2421 + </label>
2422 + <span class="mxchat-toggle-label">
2423 + <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
2424 + </span>
2425 + </div>
2426 +
2427 + <!-- Custom Post Types Section -->
2428 + <div class="mxchat-section-content">
2429 + <div class="mxchat-custom-post-types-header">
2430 + <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
2431 + <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
2432 + <span class="mxchat-toggle-icon">▼</span>
2433 + </button>
2434 + </div>
2435 +
2436 + <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
2437 + <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
2438 + <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
2439 +
2440 + <div class="mxchat-custom-post-types">
2441 + <?php
2442 + $post_types = $knowledge_manager->mxchat_get_public_post_types();
2443 + // Skip post and page as they're handled separately
2444 + unset($post_types['post']);
2445 + unset($post_types['page']);
2446 +
2447 + if (!empty($post_types)) {
2448 + foreach ($post_types as $post_type => $label) {
2449 + $option_name = 'mxchat_auto_sync_' . $post_type;
2450 + $is_enabled = get_option($option_name, '0');
2451 + ?>
2452 + <div class="mxchat-toggle-container">
2453 + <label class="mxchat-toggle-switch">
2454 + <input type="checkbox"
2455 + name="<?php echo esc_attr($option_name); ?>"
2456 + class="mxchat-autosave-field"
2457 + value="1"
2458 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2459 + <?php checked($is_enabled, '1'); ?>>
2460 + <span class="mxchat-toggle-slider"></span>
2461 + </label>
2462 + <span class="mxchat-toggle-label">
2463 + <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
2464 + </span>
2465 + </div>
2466 + <?php
2467 + }
2468 + } else {
2469 + echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
2470 + }
2471 + ?>
2472 + </div>
2473 + </div>
2474 + </div>
2475 + </div>
2476 + </div>
2477 + </div>
2478 + </div>
2479 +
2480 + <!-- Role-Based Content Restrictions Card -->
2481 + <div class="mxchat-card" style="margin-top: 20px;">
2482 + <h3><?php esc_html_e('Role-Based Content Restrictions', 'mxchat'); ?></h3>
2483 + <p class="mxchat-description">
2484 + <?php esc_html_e('Automatically restrict content access based on WordPress tags. When you assign a tag to a role, any post with that tag will only be accessible to users with that role or higher in the chatbot knowledge base.', 'mxchat'); ?>
2485 + </p>
2486 +
2487 + <!-- Info Box -->
2488 + <div class="mxchat-knowledge-warning info" style="background: #e3f2fd; border-left-color: #2196f3;">
2489 + <p>
2490 + <span class="dashicons dashicons-info"></span>
2491 + <strong><?php esc_html_e('How it works:', 'mxchat'); ?></strong>
2492 + <?php esc_html_e('Add a tag below and select which role should have access. Any content tagged with that tag will automatically be restricted to that role level. Works with both auto-sync and manual imports.', 'mxchat'); ?>
2493 + </p>
2494 + </div>
2495 +
2496 + <!-- Add New Tag-Role Mapping -->
2497 + <div class="mxchat-role-mapping-form">
2498 + <h4><?php esc_html_e('Add Tag-Role Mapping', 'mxchat'); ?></h4>
2499 +
2500 + <div class="mxchat-form-row" style="display: flex; gap: 15px; align-items: flex-end; margin-bottom: 20px;">
2501 + <div class="mxchat-form-group" style="flex: 1;">
2502 + <label for="mxchat-tag-input">
2503 + <?php esc_html_e('Tag Name', 'mxchat'); ?>
2504 + </label>
2505 + <input type="text"
2506 + id="mxchat-tag-input"
2507 + class="regular-text"
2508 + placeholder="<?php esc_attr_e('Enter tag name (e.g., premium, members-only)', 'mxchat'); ?>" />
2509 + <p class="description">
2510 + <?php esc_html_e('Enter the exact tag slug as it appears in WordPress', 'mxchat'); ?>
2511 + </p>
2512 + </div>
2513 +
2514 + <div class="mxchat-form-group" style="flex: 1;">
2515 + <label for="mxchat-role-select">
2516 + <?php esc_html_e('Required Role', 'mxchat'); ?>
2517 + </label>
2518 + <select id="mxchat-role-select" class="regular-text">
2519 + <?php
2520 + // Get role options from knowledge manager
2521 + $role_options = array(
2522 + 'public' => __('Public (Everyone)', 'mxchat'),
2523 + 'logged_in' => __('Logged In Users', 'mxchat'),
2524 + 'subscriber' => __('Subscribers & Above', 'mxchat'),
2525 + 'contributor' => __('Contributors & Above', 'mxchat'),
2526 + 'author' => __('Authors & Above', 'mxchat'),
2527 + 'editor' => __('Editors & Above', 'mxchat'),
2528 + 'administrator' => __('Administrators Only', 'mxchat')
2529 + );
2530 +
2531 + foreach ($role_options as $role_key => $role_label) : ?>
2532 + <option value="<?php echo esc_attr($role_key); ?>">
2533 + <?php echo esc_html($role_label); ?>
2534 + </option>
2535 + <?php endforeach; ?>
2536 + </select>
2537 + <p class="description">
2538 + <?php esc_html_e('Select the minimum role required to access this content', 'mxchat'); ?>
2539 + </p>
2540 + </div>
2541 +
2542 + <div style="padding-bottom: 25px;">
2543 + <button type="button"
2544 + id="mxchat-add-tag-role"
2545 + class="mxchat-button-primary">
2546 + <span class="dashicons dashicons-plus-alt"></span>
2547 + <?php esc_html_e('Add Mapping', 'mxchat'); ?>
2548 + </button>
2549 + </div>
2550 + </div>
2551 + </div>
2552 +
2553 + <!-- Existing Tag-Role Mappings -->
2554 + <div class="mxchat-role-mappings-list">
2555 + <h4><?php esc_html_e('Current Tag-Role Mappings', 'mxchat'); ?></h4>
2556 +
2557 + <div id="mxchat-mappings-container">
2558 + <!-- Mappings will be loaded here via AJAX -->
2559 + <div class="mxchat-loading-mappings">
2560 + <span class="mxchat-spinner is-active"></span>
2561 + <?php esc_html_e('Loading mappings...', 'mxchat'); ?>
2562 + </div>
2563 + </div>
2564 +
2565 + <!-- Empty State (initially hidden) -->
2566 + <div id="mxchat-no-mappings" style="display: none; text-align: center; padding: 40px; color: #666;">
2567 + <span class="dashicons dashicons-tag" style="font-size: 48px; opacity: 0.3;"></span>
2568 + <p><?php esc_html_e('No tag-role mappings yet. Add your first mapping above to get started.', 'mxchat'); ?></p>
2569 + </div>
2570 + </div>
2571 +
2572 + <!-- Bulk Update Section -->
2573 + <div class="mxchat-bulk-update-section" style="margin-top: 30px; padding-top: 30px; border-top: 1px solid #ddd;">
2574 + <h4><?php esc_html_e('Bulk Update Existing Content', 'mxchat'); ?></h4>
2575 + <p class="mxchat-description">
2576 + <?php esc_html_e('Apply role restrictions to all existing content that has the mapped tags. This is useful when setting up tag-role mappings for the first time or after changing mappings.', 'mxchat'); ?>
2577 + </p>
2578 +
2579 + <div class="mxchat-bulk-update-controls">
2580 + <button type="button"
2581 + id="mxchat-bulk-update-roles"
2582 + class="mxchat-button-secondary">
2583 + <span class="dashicons dashicons-update"></span>
2584 + <?php esc_html_e('Update All Existing Content', 'mxchat'); ?>
2585 + </button>
2586 +
2587 + <div id="mxchat-bulk-update-progress" style="display: none; margin-top: 15px;">
2588 + <div class="mxchat-progress-bar">
2589 + <div class="mxchat-progress-fill" style="width: 0%"></div>
2590 + </div>
2591 + <p class="mxchat-progress-text" style="margin-top: 10px; color: #666;">
2592 + <?php esc_html_e('Processing...', 'mxchat'); ?>
2593 + </p>
2594 + </div>
2595 +
2596 + <div id="mxchat-bulk-update-result" style="display: none; margin-top: 15px;"></div>
2597 + </div>
2598 + </div>
2599 + </div>
2600 +
2601 + </div>
1969 2602
1970 - if (is_wp_error($submission_result)) {
1971 - $batch_stats['failed']++;
1972 - $batch_stats['last_error'] = $submission_result->get_error_message();
1973 - continue;
1974 - }
2603 + <!-- Pinecone Settings Tab -->
2604 + <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
2605 + <div class="mxchat-card">
2606 + <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
2607 + <p class="mxchat-description">
2608 + <?php echo wp_kses(
2609 + __('<strong>Pinecone is optional</strong> and not required for MxChat to function. It provides enhanced search performance for larger knowledge bases. When enabled, content will be stored in Pinecone instead of the WordPress database, but you can use MxChat without it.', 'mxchat'),
2610 + array('strong' => array())
2611 + ); ?>
2612 + </p>
1975 2613
1976 - $batch_stats['processed']++;
1977 - } else {
1978 - $batch_stats['failed']++;
1979 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
1980 - }
1981 - }
2614 + <div class="mxchat-pinecone-info-box">
2615 + <div class="mxchat-info-icon">
2616 + <span class="dashicons dashicons-info"></span>
2617 + </div>
2618 + <div class="mxchat-info-content">
2619 + <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
2620 + <p><?php esc_html_e('Pinecone is a specialized vector database designed for AI applications. It provides faster similarity searches and better scalability compared to traditional databases for AI-powered features.', 'mxchat'); ?></p>
2621 + <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
2622 + </div>
2623 + </div>
1982 2624
1983 - $status['processed_urls'] = absint($i + 1);
1984 - $status['last_update'] = time();
1985 - $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
1986 - $status['last_error'] = $batch_stats['last_error'];
2625 + <div class="mxchat-database-settings-form">
2626 + <?php
2627 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
2628 + $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
2629 + ?>
1987 2630
1988 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1989 - }
2631 + <div class="mxchat-toggle-container">
2632 + <label class="mxchat-toggle-switch">
2633 + <input type="checkbox"
2634 + name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
2635 + value="1"
2636 + <?php checked($use_pinecone, '1'); ?>>
2637 + <span class="mxchat-toggle-slider"></span>
2638 + </label>
2639 + <span class="mxchat-toggle-label">
2640 + <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
2641 + </span>
2642 + </div>
1990 2643
1991 - // If all URLs in this batch failed, stop processing
1992 - if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
1993 - $status['status'] = 'error';
1994 - $status['error'] = sprintf(
1995 - 'Processing stopped: %d consecutive failures. Last error: %s',
1996 - $batch_stats['failed'],
1997 - $batch_stats['last_error']
1998 - );
1999 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2000 - return;
2001 - }
2644 + <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
2002 2645
2003 - if ($end_url < $total_urls) {
2004 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2005 - 'urls' => array_map('esc_url_raw', $urls),
2006 - 'sitemap_url' => $sitemap_url,
2007 - 'total_urls' => $total_urls,
2008 - 'batch_size' => $batch_size,
2009 - 'batch_pause' => $batch_pause,
2010 - ));
2011 - } else {
2012 - $status['status'] = 'complete';
2013 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2014 - }
2015 - } catch (\Exception $e) {
2016 - $status['status'] = 'error';
2017 - $status['error'] = sanitize_text_field($e->getMessage());
2018 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2019 - }
2020 -}
2646 + <?php if ($use_pinecone) : ?>
2647 + <div class="mxchat-knowledge-warning success">
2648 + <p><span class="dashicons dashicons-yes-alt"></span>
2649 + <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
2650 + </p>
2651 + </div>
2652 + <?php endif; ?>
2021 2653
2022 -public function get_sitemap_processing_status($sitemap_url) {
2023 - $sitemap_url = esc_url_raw($sitemap_url);
2024 - $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2654 + <div class="mxchat-form-group">
2655 + <label for="mxchat_pinecone_api_key">
2656 + <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
2657 + </label>
2658 + <input type="password"
2659 + id="mxchat_pinecone_api_key"
2660 + name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
2661 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
2662 + class="regular-text"
2663 + placeholder="pcsk_..." />
2664 + <p class="description">
2665 + <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
2666 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
2667 + </p>
2668 + </div>
2025 2669
2026 - if (!$status || !is_array($status)) {
2027 - return false;
2028 - }
2670 + <div class="mxchat-form-group">
2671 + <label for="mxchat_pinecone_environment">
2672 + <?php esc_html_e('Region', 'mxchat'); ?>
2673 + </label>
2674 + <input type="text"
2675 + id="mxchat_pinecone_environment"
2676 + name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
2677 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
2678 + placeholder="e.g., gcp-starter"
2679 + class="regular-text" />
2680 + <p class="description">
2681 + <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
2682 + </p>
2683 + </div>
2029 2684
2030 - return array(
2031 - 'total_urls' => absint($status['total_urls']),
2032 - 'processed_urls' => absint($status['processed_urls']),
2033 - 'failed_urls' => absint($status['failed_urls'] ?? 0),
2034 - 'percentage' => ($status['total_urls'] > 0)
2035 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2036 - : 0,
2037 - 'status' => sanitize_text_field($status['status']),
2038 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2039 - 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2040 - 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
2041 - );
2042 -}
2685 + <div class="mxchat-form-group">
2686 + <label for="mxchat_pinecone_index">
2687 + <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
2688 + </label>
2689 + <input type="text"
2690 + id="mxchat_pinecone_index"
2691 + name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
2692 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
2693 + placeholder="e.g., my-wordpress-vectors"
2694 + class="regular-text" />
2695 + <p class="description">
2696 + <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
2697 + </p>
2698 + </div>
2043 2699
2044 -public function mxchat_stop_processing() {
2045 - // Verify permissions
2046 - if (!current_user_can('manage_options')) {
2047 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
2048 - }
2700 + <div class="mxchat-form-group">
2701 + <label for="mxchat_pinecone_host">
2702 + <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
2703 + </label>
2704 + <input type="text"
2705 + id="mxchat_pinecone_host"
2706 + name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
2707 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
2708 + placeholder="e.g., my-index-xyz123.svc.pinecone.io"
2709 + class="regular-text" />
2710 + <p class="description">
2711 + <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
2712 + </p>
2713 + </div>
2049 2714
2050 - // Verify nonce
2051 - check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
2715 + <div class="mxchat-setup-steps">
2716 + <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
2717 + <div class="mxchat-setup-step">
2718 + <span class="step-number">1</span>
2719 + <div class="step-content">
2720 + <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
2721 + <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
2722 + </div>
2723 + </div>
2724 + <div class="mxchat-setup-step">
2725 + <span class="step-number">2</span>
2726 + <div class="step-content">
2727 + <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
2728 + <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
2729 + <ul>
2730 + <li><?php esc_html_e('Dimensions: Choose based on your embedding model - 1536 (OpenAI Ada 2, TE3 Small), 2048 (Voyage-3 Large), 3072 (TE3 Large, Gemini Embedding), 1536 (Gemini Embedding), or 768 (Gemini Embedding)', 'mxchat'); ?></li>
2731 + <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
2732 + <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
2733 + </ul>
2734 + </div>
2735 + </div>
2736 + <div class="mxchat-setup-step">
2737 + <span class="step-number">3</span>
2738 + <div class="step-content">
2739 + <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
2740 + <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
2741 + </div>
2742 + </div>
2743 + <div class="mxchat-setup-step">
2744 + <span class="step-number">4</span>
2745 + <div class="step-content">
2746 + <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
2747 + <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
2748 + </div>
2749 + </div>
2750 + <div class="mxchat-setup-step">
2751 + <span class="step-number">5</span>
2752 + <div class="step-content">
2753 + <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
2754 + <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
2755 + </div>
2756 + </div>
2757 + </div>
2052 2758
2053 - // Get the last sitemap URL and clear its transient
2054 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
2055 - if ($sitemap_url) {
2056 - delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
2057 - delete_transient('mxchat_last_sitemap_url');
2058 - }
2759 + </div>
2059 2760
2060 - // Get the last PDF URL and clear its transient
2061 - $pdf_url = get_transient('mxchat_last_pdf_url');
2062 - if ($pdf_url) {
2063 - delete_transient('mxchat_pdf_status_' . md5($pdf_url));
2064 - delete_transient('mxchat_last_pdf_url');
2065 - }
2761 + </div>
2762 + </div>
2763 + </div>
2066 2764
2067 - // Unschedule any pending sitemap events
2068 - $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
2069 - if ($timestamp) {
2070 - wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
2071 - }
2765 + </div>
2072 2766
2073 - // Redirect back with a success message
2074 - set_transient('mxchat_admin_notice_success',
2075 - esc_html__('Processing has been stopped successfully.', 'mxchat'),
2076 - 30
2077 - );
2078 - wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2079 - exit;
2080 -}
2081 -private function mxchat_sanitize_content_for_api($content) {
2082 - // Remove script, style tags, and HTML comments
2083 - $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2084 - $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2085 - $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2767 + </div>
2768 + </div>
2086 2769
2087 - // Remove all HTML tags and decode HTML entities
2088 - $content = wp_strip_all_tags($content);
2089 - $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2770 + <!-- Content Selector Modal -->
2771 + <div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
2772 + <div class="mxchat-kb-modal-content">
2773 + <div class="mxchat-kb-modal-header">
2774 + <h3>
2775 + <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
2776 + <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
2777 + </h3>
2778 + <span class="mxchat-kb-modal-close">&times;</span>
2779 + </div>
2780 + <div class="mxchat-kb-modal-filters">
2781 + <div class="mxchat-kb-search-group">
2782 + <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
2783 + </div>
2090 2784
2091 - // Trim and normalize whitespace
2092 - $content = trim(preg_replace('/\s+/', ' ', $content));
2785 + <div class="mxchat-kb-filter-group">
2786 + <select id="mxchat-kb-content-type-filter">
2787 + <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2788 + <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
2789 + <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
2790 + <?php
2791 + // Add other post types dynamically
2792 + $post_types = get_post_types(array('public' => true), 'objects');
2793 + foreach ($post_types as $post_type) {
2794 + // Skip post and page as they're already added
2795 + if (!in_array($post_type->name, array('post', 'page'))) {
2796 + echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
2797 + }
2798 + }
2799 + ?>
2800 + </select>
2093 2801
2094 - return $content;
2095 -}
2802 + <select id="mxchat-kb-content-status-filter">
2803 + <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
2804 + <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
2805 + <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
2806 + </select>
2096 2807
2808 + <select id="mxchat-kb-processed-filter">
2809 + <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
2810 + <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
2811 + <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
2812 + </select>
2813 + </div>
2814 + </div>
2097 2815
2816 + <div class="mxchat-kb-content-selection">
2817 + <div class="mxchat-kb-selection-header">
2818 + <label>
2819 + <input type="checkbox" id="mxchat-kb-select-all">
2820 + <?php esc_html_e('Select All', 'mxchat'); ?>
2821 + </label>
2822 + <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
2823 + </div>
2098 2824
2825 + <div class="mxchat-kb-content-list">
2826 + <!-- Content will be loaded here via AJAX -->
2827 + <div class="mxchat-kb-loading">
2828 + <span class="mxchat-kb-spinner is-active"></span>
2829 + <?php esc_html_e('Loading content...', 'mxchat'); ?>
2830 + </div>
2831 + </div>
2099 2832
2100 -public function mxchat_handle_product_change($post_id, $post, $update) {
2101 - if ($post->post_type !== 'product') {
2102 - return;
2103 - }
2833 + <div class="mxchat-kb-pagination">
2834 + <!-- Pagination will be added here -->
2835 + </div>
2836 + </div>
2104 2837
2105 - if ($post->post_status === 'publish') {
2106 - add_action('shutdown', function() use ($post_id) {
2107 - $product = wc_get_product($post_id);
2108 - if ($product) {
2109 - $this->mxchat_store_product_embedding($product);
2110 - }
2111 - });
2112 - }
2838 + <div class="mxchat-kb-modal-footer">
2839 + <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
2840 + <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
2841 + <span class="mxchat-kb-selected-count">(0)</span>
2842 + </button>
2843 + <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
2844 + <?php esc_html_e('Cancel', 'mxchat'); ?>
2845 + </button>
2846 + </div>
2847 + </div>
2848 + </div>
2849 +
2850 +<?php
2113 2851 }
2114 2852
2115 -private function mxchat_store_product_embedding($product) {
2116 - if (!isset($this->options['enable_woocommerce_integration']) ||
2117 - !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2118 - return;
2119 - }
2120 2853
2121 - $source_url = get_permalink($product->get_id());
2122 2854
2123 - // Build description
2124 - $regular_price = $product->get_regular_price();
2125 - $sale_price = $product->get_sale_price();
2126 - $description = $product->get_description() . "\n\n" .
2127 - esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" .
2128 - esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" .
2129 - esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" .
2130 - esc_html__('SKU:', 'mxchat') . " " . $product->get_sku();
2131 2855
2132 - // Generate embedding vector
2133 - $embedding_vector = $this->mxchat_generate_embedding($description);
2134 - if (!is_array($embedding_vector)) {
2135 - return;
2136 - }
2137 2856
2138 - // Check for Pinecone addon and its settings
2139 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2140 - $use_pinecone = false;
2141 2857
2142 - if ($pinecone_settings && is_array($pinecone_settings)) {
2143 - $use_pinecone = (
2144 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2145 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2146 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2147 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2148 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2149 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2858 +/**
2859 + * Get bot-specific Pinecone configuration
2860 + * Used in the knowledge retrieval functions
2861 + */
2862 +private function get_bot_pinecone_config($bot_id = 'default') {
2863 + //error_log("MXCHAT DEBUG: get_bot_pinecone_config called for bot: " . $bot_id);
2864 +
2865 + // If default bot or multi-bot add-on not active, use default Pinecone config
2866 + if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
2867 + //error_log("MXCHAT DEBUG: Using default Pinecone config (no multi-bot or bot is 'default')");
2868 + $addon_options = get_option('mxchat_pinecone_addon_options', array());
2869 + $config = array(
2870 + 'use_pinecone' => (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1'),
2871 + 'api_key' => $addon_options['mxchat_pinecone_api_key'] ?? '',
2872 + 'host' => $addon_options['mxchat_pinecone_host'] ?? '',
2873 + 'namespace' => $addon_options['mxchat_pinecone_namespace'] ?? ''
2150 2874 );
2875 + //error_log("MXCHAT DEBUG: Default config - use_pinecone: " . ($config['use_pinecone'] ? 'true' : 'false'));
2876 + return $config;
2151 2877 }
2152 -
2153 - if ($use_pinecone) {
2154 - $result = $this->store_in_pinecone_main(
2155 - $embedding_vector,
2156 - $description,
2157 - $source_url,
2158 - $pinecone_settings['mxchat_pinecone_api_key'],
2159 - $pinecone_settings['mxchat_pinecone_environment'],
2160 - $pinecone_settings['mxchat_pinecone_index']
2161 - );
2162 -
2163 - if (!$result['success']) {
2164 - // Fallback to WordPress DB if Pinecone storage fails
2165 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2166 - }
2878 +
2879 + //error_log("MXCHAT DEBUG: Calling filter 'mxchat_get_bot_pinecone_config' for bot: " . $bot_id);
2880 +
2881 + // Hook for multi-bot add-on to provide bot-specific Pinecone config
2882 + $bot_pinecone_config = apply_filters('mxchat_get_bot_pinecone_config', array(), $bot_id);
2883 +
2884 + if (!empty($bot_pinecone_config)) {
2885 + //error_log("MXCHAT DEBUG: Got bot-specific config from filter");
2886 + //error_log(" - use_pinecone: " . (isset($bot_pinecone_config['use_pinecone']) ? ($bot_pinecone_config['use_pinecone'] ? 'true' : 'false') : 'not set'));
2887 + //error_log(" - host: " . ($bot_pinecone_config['host'] ?? 'not set'));
2888 + //error_log(" - namespace: " . ($bot_pinecone_config['namespace'] ?? 'not set'));
2167 2889 } else {
2168 - // Use WordPress DB storage
2169 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2890 + //error_log("MXCHAT DEBUG: Filter returned empty config!");
2170 2891 }
2892 +
2893 + return is_array($bot_pinecone_config) ? $bot_pinecone_config : array();
2171 2894 }
2172 2895
2173 -public function mxchat_handle_product_delete($post_id) {
2174 - if (get_post_type($post_id) !== 'product') {
2175 - return;
2896 +public function mxchat_delete_chat_history() {
2897 + if (!current_user_can('manage_options')) {
2898 + echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
2899 + wp_die();
2176 2900 }
2901 + check_ajax_referer('mxchat_delete_chat_history', 'security');
2902 + global $wpdb;
2903 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2177 2904
2178 - $source_url = get_permalink($post_id);
2905 + if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
2906 + $deleted_count = 0;
2179 2907
2180 - // Check for Pinecone addon and its settings
2181 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2182 - $use_pinecone = false;
2908 + foreach ($_POST['delete_session_ids'] as $session_id) {
2909 + $session_id_sanitized = sanitize_text_field($session_id);
2183 2910
2184 - if ($pinecone_settings && is_array($pinecone_settings)) {
2185 - $use_pinecone = (
2186 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2187 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2188 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2189 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2190 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2191 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2192 - );
2193 - }
2911 + // Clear relevant cache before deletion
2912 + $cache_key = 'chat_session_' . $session_id_sanitized;
2913 + wp_cache_delete($cache_key, 'mxchat_chat_sessions');
2194 2914
2195 - if ($use_pinecone) {
2196 - $this->delete_from_pinecone(
2197 - array($source_url),
2198 - $pinecone_settings['mxchat_pinecone_api_key'],
2199 - $pinecone_settings['mxchat_pinecone_environment'],
2200 - $pinecone_settings['mxchat_pinecone_index']
2201 - );
2202 - } else {
2203 - // Fallback to WordPress DB deletion
2204 - global $wpdb;
2205 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2206 - $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2207 - }
2208 -}
2915 + // Perform the deletion from the database table
2916 + $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
2209 2917
2210 -/**
2211 - * Delete vectors from Pinecone by source URL
2212 - *
2213 - * @param array $urls Array of source URLs to delete
2214 - * @param string $api_key Pinecone API key
2215 - * @param string $environment Pinecone environment
2216 - * @param string $index_name Pinecone index name
2217 - * @return array Associative array with 'success' boolean and 'message' string
2218 - */
2219 -private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2220 - // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2221 - $options = get_option('mxchat_pinecone_addon_options');
2222 - $host = $options['mxchat_pinecone_host'] ?? '';
2918 + // Delete the corresponding option entry from wp_options table
2919 + delete_option("mxchat_history_" . $session_id_sanitized);
2223 2920
2224 - if (empty($host)) {
2225 - return array(
2226 - 'success' => false,
2227 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2228 - );
2229 - }
2921 + // Delete any associated metadata options
2922 + delete_option("mxchat_email_" . $session_id_sanitized);
2923 + delete_option("mxchat_agent_name_" . $session_id_sanitized);
2230 2924
2231 - // Build API endpoint using the configured host
2232 - $api_endpoint = "https://{$host}/vectors/delete";
2925 + $deleted_count++;
2926 + }
2233 2927
2234 - // Create vector IDs from URLs (matching your store method's ID generation)
2235 - $vector_ids = array_map('md5', $urls);
2928 + // Optionally, clear a general cache if you have one
2929 + wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
2236 2930
2237 - // Prepare the delete request body
2238 - $request_body = array(
2239 - 'ids' => $vector_ids,
2240 - 'filter' => array(
2241 - 'source_url' => array(
2242 - '$in' => $urls
2931 + echo wp_json_encode([
2932 + 'success' => sprintf(
2933 + esc_html__('%d chat session(s) have been deleted from all storage locations.', 'mxchat'),
2934 + $deleted_count
2243 2935 )
2244 - )
2245 - );
2246 -
2247 - // Make the deletion request
2248 - $response = wp_remote_post($api_endpoint, array(
2249 - 'headers' => array(
2250 - 'Api-Key' => $api_key,
2251 - 'accept' => 'application/json',
2252 - 'content-type' => 'application/json'
2253 - ),
2254 - 'body' => wp_json_encode($request_body),
2255 - 'timeout' => 30,
2256 - 'data_format' => 'body'
2257 - ));
2258 -
2259 - // Handle WordPress HTTP API errors
2260 - if (is_wp_error($response)) {
2261 - return array(
2262 - 'success' => false,
2263 - 'message' => $response->get_error_message()
2264 - );
2936 + ]);
2937 + } else {
2938 + echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
2265 2939 }
2266 2940
2267 - // Check response status
2268 - $response_code = wp_remote_retrieve_response_code($response);
2269 - if ($response_code !== 200) {
2270 - $body = wp_remote_retrieve_body($response);
2271 - return array(
2272 - 'success' => false,
2273 - 'message' => sprintf(
2274 - 'Pinecone API error (HTTP %d): %s',
2275 - $response_code,
2276 - $body
2277 - )
2278 - );
2941 + wp_die();
2942 +}
2943 +public function display_admin_notices() {
2944 + // Check if we're on a MXChat admin page
2945 + $screen = get_current_screen();
2946 + if (!$screen || strpos($screen->base, 'mxchat') === false) {
2947 + return;
2279 2948 }
2280 2949
2281 - // Parse response body
2282 - $body = wp_remote_retrieve_body($response);
2283 - $response_data = json_decode($body, true);
2950 + //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
2284 2951
2285 - // Final validation of the response
2286 - if (json_last_error() !== JSON_ERROR_NONE) {
2287 - return array(
2288 - 'success' => false,
2289 - 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2290 - );
2952 + // Check for error notices
2953 + $error_notice = get_transient('mxchat_admin_notice_error');
2954 + if ($error_notice) {
2955 + //error_log('Found error transient: ' . $error_notice);
2956 + echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
2957 + delete_transient('mxchat_admin_notice_error');
2958 + //error_log('Displayed and deleted error transient');
2959 + } else {
2960 + //error_log('No error transient found');
2291 2961 }
2292 2962
2293 - return array(
2294 - 'success' => true,
2295 - 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2296 - );
2297 -}
2298 -
2299 -
2300 -public function mxchat_fetch_chat_history() {
2301 - global $wpdb;
2302 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2303 -
2304 - if (!current_user_can('manage_options')) {
2305 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2963 + // Check for success notices
2964 + $success_notice = get_transient('mxchat_admin_notice_success');
2965 + if ($success_notice) {
2966 + //error_log('Found success transient: ' . $success_notice);
2967 + echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
2968 + delete_transient('mxchat_admin_notice_success');
2969 + //error_log('Displayed and deleted success transient');
2306 2970 }
2307 2971
2308 - // First get unique session IDs ordered by most recent message in each session
2309 - $session_ids = $wpdb->get_col(
2310 - $wpdb->prepare(
2311 - "SELECT DISTINCT session_id
2312 - FROM {$table_name}
2313 - GROUP BY session_id
2314 - ORDER BY MAX(timestamp) DESC"
2315 - )
2316 - );
2317 -
2318 - if (empty($session_ids)) {
2319 - wp_die(esc_html__('No chat history available.', 'mxchat'));
2972 + // Check for info notices
2973 + $info_notice = get_transient('mxchat_admin_notice_info');
2974 + if ($info_notice) {
2975 + //error_log('Found info transient: ' . $info_notice);
2976 + echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
2977 + delete_transient('mxchat_admin_notice_info');
2978 + //error_log('Displayed and deleted info transient');
2320 2979 }
2321 2980
2322 - ob_start();
2323 - echo '<div class="mxchat-transcript">';
2324 -
2325 - // Iterate through sessions from newest to oldest
2326 - foreach ($session_ids as $session_id) {
2327 - // Get the email associated with this session (if available)
2328 - $email = $wpdb->get_var(
2329 - $wpdb->prepare(
2330 - "SELECT user_email
2331 - FROM {$table_name}
2332 - WHERE session_id = %s AND user_email != ''
2333 - ORDER BY timestamp ASC
2334 - LIMIT 1",
2335 - $session_id
2336 - )
2337 - );
2338 -
2339 - // Debug: Check email retrieval
2340 - if (empty($email)) {
2341 - //error_log("No email found for session {$session_id}");
2342 - } else {
2343 - //error_log("Email for session {$session_id}: " . $email);
2344 - }
2345 -
2346 - // Get messages for this session ordered by timestamp
2347 - $messages = $wpdb->get_results(
2348 - $wpdb->prepare(
2349 - "SELECT * FROM {$table_name}
2350 - WHERE session_id = %s
2351 - ORDER BY timestamp ASC",
2352 - $session_id
2353 - )
2354 - );
2355 -
2356 - // Start session block
2357 - echo '<div class="mxchat-session">';
2358 - echo '<div class="mxchat-session-header">';
2359 - // Wrap checkbox and session ID in one block
2360 - echo '<div class="mxchat-session-id">';
2361 - echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
2362 - echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
2363 - echo '</div>';
2364 -
2365 - // Place email directly below the session ID block
2366 - if (!empty($email)) {
2367 - echo '<div class="mxchat-session-email">';
2368 - echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
2369 - echo '</div>';
2370 - }
2371 - echo '</div>';
2372 -
2373 - echo '<div class="mxchat-messages">';
2374 -
2375 - // Display messages for this session
2376 - foreach ($messages as $transcript) {
2377 - $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
2378 -
2379 - // Determine message styling
2380 - switch ($transcript->role) {
2381 - case 'assistant':
2382 - case 'bot':
2383 - $message_class = 'bot-message';
2384 - $display_role = esc_html__('Chatbot', 'mxchat');
2385 - break;
2386 - case 'user':
2387 - $message_class = 'user-message';
2388 - $display_role = !empty($transcript->user_identifier)
2389 - ? sanitize_text_field($transcript->user_identifier)
2390 - : esc_html__('User', 'mxchat');
2391 - break;
2392 - case 'agent':
2393 - $message_class = 'agent-message';
2394 - $display_role = esc_html__('Agent', 'mxchat');
2395 - break;
2396 - default:
2397 - $message_class = 'unknown-message';
2398 - $display_role = esc_html__('Unknown', 'mxchat');
2399 - }
2400 -
2401 - // Process message content
2402 - $message_content = wp_kses(
2403 - stripslashes($transcript->message),
2404 - [
2405 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2406 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2407 - 'a' => ['href' => [], 'title' => []]
2408 - ]
2409 - );
2410 - $message_content = nl2br($message_content);
2411 -
2412 - // Render message
2413 - echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
2414 - echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
2415 - echo '<div class="mxchat-message-content">' . $message_content . '</div>';
2416 - echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
2417 - echo '</div>';
2418 - }
2419 -
2420 - // Close session block
2421 - echo '</div></div>';
2422 - }
2423 -
2424 - echo '</div>';
2425 - $output = ob_get_clean();
2426 - echo $output;
2427 - wp_die();
2428 2981 }
2429 2982
2430 2983
2431 2984 public function mxchat_create_activation_page() {
@@ -2430,16 +2983,26 @@
2430 2983
2431 2984 public function mxchat_create_activation_page() {
2432 2985 $license_status = get_option('mxchat_license_status', 'inactive');
2433 2986 $license_error = get_option('mxchat_license_error', '');
2987 + $current_domain = parse_url(home_url(), PHP_URL_HOST);
2988 +
2989 + $license_management_url = 'https://mxchat.ai/my-account/orders/';
2990 +
2991 + // Check if this activation has been linked to a domain
2992 + $is_domain_linked = $this->is_current_activation_linked($current_domain);
2434 2993 ?>
2435 2994 <div class="wrap mxchat-admin-activation">
2436 2995 <div class="mxchat-pro-hero">
2437 2996 <h1 class="pro-title">
2438 - <span class="pro-gradient-text">Activate</span> MxChat Pro
2997 + <span class="pro-gradient-text">Manage</span> MxChat Pro
2439 2998 </h1>
2440 2999 <p class="pro-subtitle">
2441 - <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
3000 + <?php if ($license_status === 'active'): ?>
3001 + <?php esc_html_e('Your pro license is active. Manage your activation below.', 'mxchat'); ?>
3002 + <?php else: ?>
3003 + <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
3004 + <?php endif; ?>
2442 3005 </p>
2443 3006 </div>
2444 3007
2445 3008 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
@@ -2447,8 +3010,25 @@
2447 3010 <p><?php echo esc_html($license_error); ?></p>
2448 3011 </div>
2449 3012 <?php endif; ?>
2450 3013
3014 + <!-- Domain Linking Notice for Existing Users -->
3015 + <?php if ($license_status === 'active' && !$is_domain_linked): ?>
3016 + <div class="notice notice-info">
3017 + <p>
3018 + <strong><?php esc_html_e('New Feature: Domain Tracking', 'mxchat'); ?></strong><br>
3019 + <?php esc_html_e('We\'ve enhanced our license system to track your activated domains. Link this activation to your domain for better license management.', 'mxchat'); ?>
3020 + </p>
3021 + <p>
3022 + <button type="button" id="link-domain-button" class="button button-secondary">
3023 + <?php esc_html_e('Link This Domain', 'mxchat'); ?>
3024 + </button>
3025 + <span id="domain-link-status" style="margin-left: 10px;"></span>
3026 + </p>
3027 + </div>
3028 + <?php endif; ?>
3029 +
3030 + <!-- Activation Form -->
2451 3031 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2452 3032 <div class="mxchat-pro-form-container">
2453 3033 <table class="form-table">
2454 3034 <tr valign="top">
@@ -2463,8 +3043,12 @@
2463 3043 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text mxchat-pro-input" required />
2464 3044 </td>
2465 3045 </tr>
2466 3046 </table>
3047 +
3048 + <!-- Hidden field for domain -->
3049 + <input type="hidden" id="mxchat_domain" name="domain" value="<?php echo esc_attr($current_domain); ?>" />
3050 +
2467 3051 <?php if ($license_status !== 'active'): ?>
2468 3052 <div class="mxchat-pro-button-container">
2469 3053 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2470 3054 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
@@ -2471,8 +3055,9 @@
2471 3055 </div>
2472 3056 <?php endif; ?>
2473 3057 </div>
2474 3058 </form>
3059 +
2475 3060 <!-- License Status Display -->
2476 3061 <div class="mxchat-pro-status">
2477 3062 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2478 3063 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
@@ -2478,15 +3063,91 @@
2478 3063 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2479 3064 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2480 3065 </span>
2481 3066 </h3>
3067 +
3068 + <?php if ($license_status === 'active'): ?>
3069 + <div class="mxchat-license-details">
3070 + <p><strong><?php esc_html_e('Domain:', 'mxchat'); ?></strong> <?php echo esc_html($current_domain); ?>
3071 + <?php if ($is_domain_linked): ?>
3072 + <span style="color: green; margin-left: 10px;">✓ <?php esc_html_e('Linked', 'mxchat'); ?></span>
3073 + <?php else: ?>
3074 + <span style="color: orange; margin-left: 10px;">⚠ <?php esc_html_e('Not Linked', 'mxchat'); ?></span>
3075 + <?php endif; ?>
3076 + </p>
3077 + <p><strong><?php esc_html_e('Email:', 'mxchat'); ?></strong> <?php echo esc_html(get_option('mxchat_pro_email')); ?></p>
3078 + <p><strong><?php esc_html_e('License Key:', 'mxchat'); ?></strong>
3079 + <code style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px;">
3080 + <?php echo esc_html(get_option('mxchat_activation_key')); ?>
3081 + </code>
3082 + </p>
3083 + <p>
3084 + <small>
3085 + <?php esc_html_e('Manage all your licenses and domains on', 'mxchat'); ?>
3086 + <a href="<?php echo esc_url($license_management_url); ?>" target="_blank">
3087 + <?php esc_html_e('your account dashboard', 'mxchat'); ?>
3088 + </a>
3089 + </small>
3090 + </p>
3091 +
3092 + <!-- Deactivation Section -->
3093 + <div class="mxchat-deactivation-section" style="margin-top: 20px; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
3094 + <h4 style="margin-top: 0; color: #856404;">
3095 + <?php esc_html_e('License Management', 'mxchat'); ?>
3096 + </h4>
3097 + <p style="margin-bottom: 15px; color: #856404;">
3098 + <?php esc_html_e('Need to move this license to another site? Deactivate it here first, then activate it on your new site.', 'mxchat'); ?>
3099 + </p>
3100 + <button type="button" id="deactivate-license-button" class="button button-secondary" style="background: #dc3545; color: white; border-color: #dc3545;">
3101 + 🔓 <?php esc_html_e('Deactivate License', 'mxchat'); ?>
3102 + </button>
3103 + <span id="deactivate-status" style="margin-left: 10px;"></span>
3104 + </div>
3105 + </div>
3106 + <?php endif; ?>
2482 3107 </div>
2483 3108
3109 + <!-- Hidden fields for JavaScript -->
3110 + <input type="hidden" id="mxchat-ajax-url" value="<?php echo admin_url('admin-ajax.php'); ?>" />
3111 + <input type="hidden" id="mxchat-nonce" value="<?php echo wp_create_nonce('mxchat_activate_license_nonce'); ?>" />
3112 + <input type="hidden" id="mxchat-api-url" value="https://mxchat.ai" />
2484 3113 </div>
2485 3114 <?php
2486 3115 }
2487 3116
2488 -public function mxchat_intents_page_html() {
3117 +/**
3118 + * Check if current activation is linked to a domain
3119 + * This checks YOUR website's database, not the user's local database
3120 + */
3121 +private function is_current_activation_linked($domain) {
3122 + $license_key = get_option('mxchat_activation_key');
3123 + $email = get_option('mxchat_pro_email');
3124 +
3125 + if (empty($license_key) || empty($email)) {
3126 + return false;
3127 + }
3128 +
3129 + // Check with YOUR website's API
3130 + $response = wp_remote_post('https://mxchat.ai/mxchat-api/check-domain', array(
3131 + 'body' => array(
3132 + 'license_key' => $license_key,
3133 + 'email' => $email,
3134 + 'domain' => $domain
3135 + ),
3136 + 'timeout' => 10,
3137 + 'sslverify' => false
3138 + ));
3139 +
3140 + if (is_wp_error($response)) {
3141 + return false;
3142 + }
3143 +
3144 + $body = json_decode(wp_remote_retrieve_body($response), true);
3145 + return isset($body['success']) && $body['success'] && isset($body['data']['linked']) && $body['data']['linked'];
3146 +}
3147 +
3148 +
3149 +public function mxchat_actions_page_html() {
2489 3150 if (!current_user_can('manage_options')) {
2490 3151 return;
2491 3152 }
2492 3153
@@ -2499,9 +3160,9 @@
2499 3160
2500 3161 // Success message
2501 3162 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2502 3163 echo '<div class="notice notice-success is-dismissible"><p>' .
2503 - esc_html__('Intent updated successfully.', 'mxchat') .
3164 + esc_html__('Action updated successfully.', 'mxchat') .
2504 3165 '</p></div>';
2505 3166 }
2506 3167
2507 3168 // Filtering logic
@@ -2522,10 +3183,10 @@
2522 3183 // Pagination
2523 3184 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2524 3185 $total_pages = ceil($total_intents / $per_page);
2525 3186
2526 - // Get intents
2527 - $intents = $wpdb->get_results($wpdb->prepare(
3187 + // Get intents (now called actions)
3188 + $actions = $wpdb->get_results($wpdb->prepare(
2528 3189 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2529 3190 $per_page, $offset
2530 3191 ));
2531 3192
@@ -2536,629 +3197,1089 @@
2536 3197 <div class="wrap mxchat-wrapper">
2537 3198 <!-- Hero Section -->
2538 3199 <div class="mxchat-hero">
2539 3200 <h1 class="mxchat-main-title">
2540 - <span class="mxchat-gradient-text">Intent</span> Manager
3201 + <span class="mxchat-gradient-text">Actions</span> Manager
2541 3202 </h1>
2542 3203 <p class="mxchat-hero-subtitle">
2543 - <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
3204 + <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
2544 3205 </p>
2545 3206 </div>
2546 3207
2547 - <div class="mxchat-content">
2548 - <!-- Add Intent Card -->
2549 - <div class="mxchat-card">
2550 - <div class="mxchat-card-header">
2551 - <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2552 - </div>
2553 -
2554 - <div class="mxchat-intent-documentation">
2555 - <p>
2556 - <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2557 - <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2558 - <?php esc_html_e('documentation', 'mxchat'); ?>
2559 - </a>
2560 - <?php esc_html_e(' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat'); ?>
2561 - </p>
2562 - </div>
2563 -
2564 - <form id="mxchat-add-intent-form" method="post"
2565 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2566 - <input type="hidden" name="action" value="mxchat_add_intent">
2567 - <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2568 -
2569 - <div class="mxchat-form-group">
2570 - <label for="intent_label">
2571 - <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2572 - </label>
2573 - <input name="intent_label" type="text" id="intent_label" required
2574 - class="mxchat-intent-input"
2575 - placeholder="<?php esc_attr_e('Example Email Capture: Newsletter Signup', 'mxchat'); ?>">
3208 + <!-- Actions Header with Search and Filter -->
3209 + <div class="mxchat-actions-header">
3210 + <div class="mxchat-actions-filters">
3211 + <form method="get" class="mxchat-search-form">
3212 + <input type="hidden" name="page" value="mxchat-actions">
3213 + <div class="mxchat-search-group">
3214 + <span class="dashicons dashicons-search"></span>
3215 + <input type="text" name="s" class="mxchat-search-input"
3216 + placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
3217 + value="<?php echo esc_attr($search_term); ?>">
2576 3218 </div>
2577 -
2578 - <div class="mxchat-form-group">
2579 - <label for="phrases">
2580 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2581 - </label>
2582 - <textarea name="phrases" id="phrases" rows="5" required
2583 - class="mxchat-intent-textarea"
2584 - placeholder="<?php esc_attr_e('Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed', 'mxchat'); ?>"></textarea>
2585 - </div>
2586 -
2587 - <div class="mxchat-form-group">
2588 - <label for="callback_function">
2589 - <?php esc_html_e('Callback Function', 'mxchat'); ?>
2590 - </label>
2591 - <select name="callback_function" id="callback_function"
2592 - class="mxchat-intent-select" required>
2593 - <option value="">
2594 - <?php esc_html_e('Select a Callback', 'mxchat'); ?>
3219 + <select name="callback_filter" class="mxchat-action-filter">
3220 + <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
3221 + <?php foreach ($available_callbacks as $function => $callback_data) :
3222 + $label = $callback_data['label']; ?>
3223 + <option value="<?php echo esc_attr($function); ?>"
3224 + <?php selected($callback_filter, $function); ?>>
3225 + <?php echo esc_html($label); ?>
2595 3226 </option>
2596 - <?php
2597 - $groups = $this->mxchat_get_available_callbacks(true);
2598 - foreach ($groups as $group_label => $group_callbacks) :
2599 - echo '<optgroup label="' . esc_attr($group_label) . '">';
2600 - foreach ($group_callbacks as $function => $data) :
2601 - $label = $data['label'];
2602 - $pro_only = $data['pro_only'];
2603 - $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2604 - $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2605 - ?>
2606 - <option value="<?php echo esc_attr($function); ?>"
2607 - <?php echo $disabled; ?>>
2608 - <?php echo esc_html($label . $label_suffix); ?>
2609 - </option>
2610 - <?php
2611 - endforeach;
2612 - echo '</optgroup>';
2613 - endforeach;
2614 - ?>
2615 - </select>
2616 - </div>
2617 -
2618 - <button type="submit" class="mxchat-button-primary">
2619 - <?php esc_html_e('Add Intent', 'mxchat'); ?>
3227 + <?php endforeach; ?>
3228 + </select>
3229 + <button type="submit" class="mxchat-button-secondary">
3230 + <?php esc_html_e('Filter', 'mxchat'); ?>
2620 3231 </button>
2621 3232 </form>
2622 3233 </div>
3234 + <div class="mxchat-actions-controls">
3235 + <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
3236 + <span class="dashicons dashicons-plus-alt"></span>
3237 + <?php esc_html_e('Add New Action', 'mxchat'); ?>
3238 + </button>
3239 + </div>
3240 + </div>
2623 3241
2624 - <!-- Manage Intents Card -->
2625 - <div class="mxchat-card">
2626 - <div class="mxchat-card-header">
2627 - <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2628 - <div class="mxchat-header-actions">
2629 - <form method="get" class="mxchat-search-form">
2630 - <input type="hidden" name="page" value="mxchat-intents">
2631 - <div class="mxchat-search-group">
2632 - <span class="dashicons dashicons-search"></span>
2633 - <input type="text" name="s"
2634 - placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2635 - value="<?php echo esc_attr($search_term); ?>">
2636 - <select name="callback_filter" class="mxchat-intent-filter">
2637 - <option value="">
2638 - <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2639 - </option>
2640 - <?php foreach ($available_callbacks as $function => $callback_data) :
2641 - $label = $callback_data['label']; ?>
2642 - <option value="<?php echo esc_attr($function); ?>"
2643 - <?php selected($callback_filter, $function); ?>>
2644 - <?php echo esc_html($label); ?>
2645 - </option>
2646 - <?php endforeach; ?>
2647 - </select>
2648 - <button type="submit" class="mxchat-button-secondary">
2649 - <?php esc_html_e('Filter', 'mxchat'); ?>
2650 - </button>
3242 + <!-- Actions Grid Layout - All actions in a single grid -->
3243 + <div class="mxchat-actions-grid">
3244 + <div class="mxchat-cards-container">
3245 + <?php if (!empty($actions)) : ?>
3246 + <?php foreach ($actions as $action) :
3247 + $callback_function = $action->callback_function;
3248 + $callback_label = isset($available_callbacks[$callback_function]['label'])
3249 + ? $available_callbacks[$callback_function]['label']
3250 + : $callback_function;
3251 + $threshold_value = isset($action->similarity_threshold)
3252 + ? round($action->similarity_threshold * 100)
3253 + : 85;
3254 +
3255 + // Check if this is a form action
3256 + $is_form_action = strpos($action->intent_label, 'Form ') === 0;
3257 +
3258 + // Get action status (enabled/disabled) - default to true if column doesn't exist
3259 + $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
3260 +
3261 + // Get enabled bots for display
3262 + $enabled_bots = [];
3263 + if (isset($action->enabled_bots) && !empty($action->enabled_bots)) {
3264 + $enabled_bots = json_decode($action->enabled_bots, true);
3265 + if (!is_array($enabled_bots)) {
3266 + $enabled_bots = ['default'];
3267 + }
3268 + } else {
3269 + $enabled_bots = ['default']; // Backward compatibility
3270 + }
3271 + ?>
3272 + <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
3273 + <div class="mxchat-card-header">
3274 + <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
3275 + <div class="mxchat-card-toggle">
3276 + <label class="mxchat-switch">
3277 + <input type="checkbox" class="mxchat-action-toggle"
3278 + data-action-id="<?php echo esc_attr($action->id); ?>"
3279 + <?php checked($is_enabled); ?>>
3280 + <span class="mxchat-slider round"></span>
3281 + </label>
3282 + </div>
2651 3283 </div>
2652 - </form>
2653 - </div>
2654 - </div>
2655 3284
2656 - <div class="mxchat-table-wrapper">
2657 - <table class="mxchat-records-table">
2658 - <thead>
2659 - <tr>
2660 - <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2661 - <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2662 - <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2663 - <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2664 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2665 - </tr>
2666 - </thead>
2667 - <tbody>
2668 - <?php if ($intents) :
2669 - foreach ($intents as $intent) :
2670 - $callback_function = $intent->callback_function;
2671 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2672 - ? $available_callbacks[$callback_function]['label']
2673 - : $callback_function;
2674 - $threshold_value = isset($intent->similarity_threshold)
2675 - ? round($intent->similarity_threshold * 100)
2676 - : 85;
2677 -
2678 - // Check if this is a form intent (its intent_label starts with "Form ")
2679 - $is_form_intent = strpos($intent->intent_label, 'Form ') === 0;
3285 + <div class="mxchat-card-body">
3286 + <div class="mxchat-card-description">
3287 + <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
3288 + <?php echo esc_html($callback_label); ?>
3289 + </div>
3290 +
3291 + <div class="mxchat-card-phrases">
3292 + <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
3293 + <div class="mxchat-phrases-preview">
3294 + <?php
3295 + // Check if the helper function exists, otherwise use a simple substring
3296 + if (method_exists($this, 'get_trimmed_phrases')) {
3297 + echo esc_html($this->get_trimmed_phrases($action->phrases));
3298 + } else {
3299 + echo esc_html(strlen($action->phrases) > 100 ?
3300 + substr($action->phrases, 0, 97) . '...' :
3301 + $action->phrases);
3302 + }
2680 3303 ?>
2681 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2682 - <td>
2683 - <?php
2684 - if ($is_form_intent) {
2685 - // Attempt to extract the form ID from the intent_label.
2686 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2687 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2688 - if ($form_id) {
2689 - global $wpdb;
2690 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2691 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2692 - if ($form && !empty($form->title)) {
2693 - echo esc_html($form->title);
2694 - } else {
2695 - echo esc_html($intent->intent_label);
2696 - }
2697 - } else {
2698 - echo esc_html($intent->intent_label);
2699 - }
2700 - echo ' <span class="mxchat-badge">Form Intent</span>';
3304 + </div>
3305 + </div>
3306 +
3307 + <div class="mxchat-threshold-control">
3308 + <div class="mxchat-threshold-label">
3309 + <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
3310 + <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
3311 + </div>
3312 + </div>
3313 +
3314 + <!-- Bot Availability Display (Read-only) -->
3315 + <div class="mxchat-card-bots">
3316 + <strong><?php esc_html_e('Assigned bots:', 'mxchat'); ?></strong>
3317 + <div class="mxchat-bot-badges">
3318 + <?php
3319 + foreach ($enabled_bots as $bot_id) {
3320 + if ($bot_id === 'default') {
3321 + echo '<span class="mxchat-bot-badge">' . esc_html__('Default Bot', 'mxchat') . '</span>';
3322 + } else {
3323 + // Try to get bot name from multi-bot manager
3324 + if (class_exists('MxChat_Multi_Bot_Core_Manager')) {
3325 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
3326 + $available_bots = $multi_bot_manager->get_available_bots();
3327 + $bot_name = isset($available_bots[$bot_id]) ? $available_bots[$bot_id] : $bot_id;
3328 + echo '<span class="mxchat-bot-badge">' . esc_html($bot_name) . '</span>';
2701 3329 } else {
2702 - echo esc_html($intent->intent_label);
3330 + echo '<span class="mxchat-bot-badge">' . esc_html($bot_id) . '</span>';
2703 3331 }
2704 - ?>
2705 - </td>
2706 - <td class="mxchat-content-cell">
2707 - <?php echo esc_html($intent->phrases); ?>
2708 - </td>
2709 - <td><?php echo esc_html($callback_label); ?></td>
2710 - <td>
2711 - <!-- Similarity threshold adjustment (shown for all intents) -->
2712 - <form method="post"
2713 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2714 - class="mxchat-threshold-form">
2715 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2716 - <input type="hidden" name="action"
2717 - value="mxchat_update_intent_threshold">
2718 - <input type="hidden" name="intent_id"
2719 - value="<?php echo esc_attr($intent->id); ?>">
2720 - <div class="mxchat-slider-group">
2721 - <input type="range"
2722 - name="intent_threshold"
2723 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2724 - min="70"
2725 - max="95"
2726 - value="<?php echo esc_attr($threshold_value); ?>"
2727 - class="mxchat-intent-slider"
2728 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2729 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2730 - class="mxchat-intent-output">
2731 - <?php echo esc_html($threshold_value); ?>%
2732 - </output>
2733 - </div>
2734 - <button type="submit" class="mxchat-button-icon">
2735 - <span class="dashicons dashicons-saved"></span>
2736 - </button>
2737 - </form>
2738 - </td>
2739 - <td class="mxchat-actions-cell">
2740 - <?php if (!$is_form_intent) : ?>
2741 - <!-- Standard intents: allow edit and delete -->
2742 - <button type="button"
2743 - class="mxchat-button-icon mxchat-edit-button"
2744 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2745 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2746 - <span class="dashicons dashicons-edit"></span>
2747 - </button>
2748 - <form method="post"
2749 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2750 - class="mxchat-delete-form"
2751 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2752 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2753 - <input type="hidden" name="action" value="mxchat_delete_intent">
2754 - <input type="hidden" name="intent_id"
2755 - value="<?php echo esc_attr($intent->id); ?>">
2756 - <button type="submit" class="mxchat-button-icon">
2757 - <span class="dashicons dashicons-trash"></span>
2758 - </button>
2759 - </form>
2760 - <?php else : ?>
2761 - <!-- Form intents: show manage in forms button -->
2762 - <?php
2763 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2764 - $form_id = isset($matches[1]) ? $matches[1] : '';
2765 - ?>
2766 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2767 - class="mxchat-button-secondary">
2768 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2769 - </a>
2770 - <?php endif; ?>
2771 - </td>
2772 - </tr>
2773 - <?php endforeach;
2774 - else : ?>
2775 - <tr>
2776 - <td colspan="5" class="mxchat-no-records">
2777 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2778 - </td>
2779 - </tr>
2780 - <?php endif; ?>
2781 - </tbody>
2782 - </table>
2783 - </div>
3332 + }
3333 + }
3334 + ?>
3335 + </div>
3336 + </div>
3337 + </div>
2784 3338
2785 - <?php if ($total_pages > 1) : ?>
2786 - <div class="mxchat-pagination">
2787 - <?php
2788 - echo paginate_links(array(
2789 - 'base' => add_query_arg('paged', '%#%'),
2790 - 'format' => '',
2791 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
2792 - 'next_text' => __('Next &raquo;', 'mxchat'),
2793 - 'total' => $total_pages,
2794 - 'current' => $page
2795 - ));
2796 - ?>
3339 + <div class="mxchat-card-footer">
3340 + <?php
3341 + // Check if it's a form action
3342 + $is_form_action = preg_match('/Form (\d+)/', $action->intent_label, $form_matches);
3343 +
3344 + // Check if it's a recommendation flow action
3345 + $is_flow_action = preg_match('/Recommendation Flow (\d+)/', $action->intent_label, $flow_matches);
3346 +
3347 + if ($is_form_action) {
3348 + $form_id = isset($form_matches[1]) ? $form_matches[1] : '';
3349 + ?>
3350 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
3351 + class="mxchat-button-primary">
3352 + <span class="dashicons dashicons-feedback"></span>
3353 + <?php esc_html_e('Edit Form', 'mxchat'); ?>
3354 + </a>
3355 + <?php } elseif ($is_flow_action) {
3356 + ?>
3357 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-smart-recommender')); ?>"
3358 + class="mxchat-button-primary">
3359 + <span class="dashicons dashicons-list-view"></span>
3360 + <?php esc_html_e('Manage Flows', 'mxchat'); ?>
3361 + </a>
3362 + <?php } else { ?>
3363 + <button type="button"
3364 + class="mxchat-button-secondary mxchat-edit-button"
3365 + data-action-id="<?php echo esc_attr($action->id); ?>"
3366 + data-phrases="<?php echo esc_attr($action->phrases); ?>"
3367 + data-label="<?php echo esc_attr($action->intent_label); ?>"
3368 + data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
3369 + data-callback-function="<?php echo esc_attr($action->callback_function); ?>"
3370 + data-enabled-bots="<?php echo esc_attr(json_encode($enabled_bots)); ?>">
3371 + <span class="dashicons dashicons-edit"></span>
3372 + <?php esc_html_e('Edit', 'mxchat'); ?>
3373 + </button>
3374 + <form method="post"
3375 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
3376 + class="mxchat-delete-form"
3377 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
3378 + <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
3379 + <input type="hidden" name="action" value="mxchat_delete_intent">
3380 + <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
3381 + <button type="submit" class="mxchat-button-text mxchat-delete-button">
3382 + <span class="dashicons dashicons-trash"></span>
3383 + <?php esc_html_e('Delete', 'mxchat'); ?>
3384 + </button>
3385 + </form>
3386 + <?php } ?>
3387 + </div>
3388 + </div>
3389 + <?php endforeach; ?>
3390 + <?php else : ?>
3391 + <!-- If no actions found -->
3392 + <div class="mxchat-no-actions">
3393 + <div class="mxchat-empty-state">
3394 + <span class="dashicons dashicons-format-chat"></span>
3395 + <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
3396 + <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
3397 + <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
3398 + <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
3399 + </button>
3400 + </div>
2797 3401 </div>
2798 3402 <?php endif; ?>
2799 3403 </div>
2800 3404 </div>
2801 3405
2802 - <!-- Edit Modal -->
2803 -<div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
3406 + <?php if ($total_pages > 1) : ?>
3407 + <div class="mxchat-pagination">
3408 + <?php
3409 + echo paginate_links(array(
3410 + 'base' => add_query_arg('paged', '%#%'),
3411 + 'format' => '',
3412 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
3413 + 'next_text' => __('Next &raquo;', 'mxchat'),
3414 + 'total' => $total_pages,
3415 + 'current' => $page
3416 + ));
3417 + ?>
3418 + </div>
3419 + <?php endif; ?>
3420 +
3421 +<!-- Add/Edit Action Modal with Step-Based Approach -->
3422 +<!-- Complete Modal HTML with Defined Groups Variable -->
3423 +<div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
2804 3424 <div class="mxchat-modal-content">
2805 3425 <span class="mxchat-modal-close">&times;</span>
2806 - <div class="mxchat-card-header">
2807 - <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2808 - </div>
2809 3426
2810 - <form id="mxchat-edit-intent-form" method="post"
2811 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2812 - <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2813 - <input type="hidden" name="action" value="mxchat_edit_intent">
2814 - <input type="hidden" name="intent_id" id="edit_intent_id">
3427 + <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3428 + <!-- Dynamic nonce field -->
3429 + <div id="action-nonce-container">
3430 + <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
3431 + </div>
3432 + <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
3433 + <input type="hidden" name="intent_id" id="edit_action_id" value="">
3434 + <input type="hidden" name="callback_function" id="callback_function" value="">
2815 3435
2816 - <div class="mxchat-form-group">
2817 - <label for="edit_phrases">
2818 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2819 - </label>
2820 - <textarea name="phrases"
2821 - id="edit_phrases"
2822 - rows="5"
2823 - class="mxchat-intent-textarea"
2824 - required></textarea>
3436 + <!-- Step 1: Action Type Selection -->
3437 + <div id="mxchat-action-step-1" class="mxchat-action-step active">
3438 + <div class="mxchat-step-indicator">
3439 + <div class="mxchat-step-number">1</div>
3440 + <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
3441 + </div>
3442 +
3443 + <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
3444 + <div class="mxchat-action-type-search">
3445 + <span class="dashicons dashicons-search"></span>
3446 + <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
3447 + </div>
3448 +
3449 + <?php
3450 + // Get the callbacks - IMPORTANT: Define the $groups variable here
3451 + $groups = $this->mxchat_get_available_callbacks(true, true);
3452 + ?>
3453 +
3454 + <div class="mxchat-action-type-categories">
3455 + <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
3456 + <?php
3457 + // Get unique categories from the defined groups
3458 + foreach ($groups as $group_label => $group_callbacks) :
3459 + $category_slug = sanitize_title($group_label);
3460 + ?>
3461 + <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
3462 + <?php endforeach; ?>
3463 + </div>
3464 +
3465 + <div class="mxchat-action-types-grid">
3466 + <?php
3467 + // Generate action cards from available callbacks
3468 + foreach ($groups as $group_label => $group_callbacks) :
3469 + $category_slug = sanitize_title($group_label);
3470 +
3471 + foreach ($group_callbacks as $function => $data) :
3472 + $label = $data['label'];
3473 + $pro_only = $data['pro_only'];
3474 + $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
3475 + $description = isset($data['description']) ? $data['description'] : '';
3476 + $is_addon = isset($data['addon']) && $data['addon'] !== false;
3477 + $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
3478 + $is_installed = isset($data['installed']) ? $data['installed'] : true;
3479 +
3480 + // Determine card status and styling
3481 + $card_class = 'mxchat-action-type-card';
3482 + $icon_class = 'mxchat-action-type-icon';
3483 + $status_badge = '';
3484 +
3485 + if ($pro_only && !$this->is_activated) {
3486 + // Pro feature but no Pro license
3487 + $icon_class .= ' pro-feature';
3488 + $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
3489 + }
3490 +
3491 + if ($is_addon && !$is_installed) {
3492 + // Add-on not installed
3493 + $card_class .= ' not-installed';
3494 + $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3495 + }
3496 +
3497 + // Default description if none provided
3498 + if (empty($description)) {
3499 + $description = sprintf(
3500 + esc_html__('Use the %s action in your chatbot', 'mxchat'),
3501 + $label
3502 + );
3503 + }
3504 + ?>
3505 + <div class="<?php echo esc_attr($card_class); ?>"
3506 + data-category="<?php echo esc_attr($category_slug); ?>"
3507 + data-value="<?php echo esc_attr($function); ?>"
3508 + data-label="<?php echo esc_attr($label); ?>"
3509 + data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
3510 + data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
3511 + data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
3512 + <div class="<?php echo esc_attr($icon_class); ?>">
3513 + <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
3514 + </div>
3515 + <div class="mxchat-action-type-info">
3516 + <h4><?php echo esc_html($label); ?></h4>
3517 + <p><?php echo esc_html($description); ?></p>
3518 + <?php if (!empty($status_badge)) : ?>
3519 + <?php echo $status_badge; ?>
3520 + <?php endif; ?>
3521 +
3522 + <?php if ($is_addon && !$is_installed) : ?>
3523 + <div class="mxchat-addon-info">
3524 + <?php echo esc_html(sprintf(
3525 + __('Requires %s', 'mxchat'),
3526 + $addon_name
3527 + )); ?>
3528 + </div>
3529 + <?php endif; ?>
3530 + </div>
3531 + </div>
3532 + <?php endforeach;
3533 + endforeach; ?>
3534 + </div>
3535 + </div>
3536 +
3537 + <div class="mxchat-modal-actions">
3538 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3539 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3540 + </button>
3541 + </div>
2825 3542 </div>
2826 3543
2827 - <div class="mxchat-modal-actions">
2828 - <button type="submit" class="mxchat-button-primary">
2829 - <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2830 - </button>
2831 - <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2832 - <?php esc_html_e('Cancel', 'mxchat'); ?>
2833 - </button>
3544 + <!-- Step 2: Action Configuration -->
3545 + <div id="mxchat-action-step-2" class="mxchat-action-step">
3546 + <div class="mxchat-step-indicator">
3547 + <div class="mxchat-step-number">2</div>
3548 + <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
3549 + </div>
3550 +
3551 + <div class="mxchat-selected-action">
3552 + <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
3553 + <span class="dashicons dashicons-arrow-left-alt"></span>
3554 + <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
3555 + </button>
3556 + <div class="mxchat-selected-action-info">
3557 + <div id="selected-action-icon" class="mxchat-action-type-icon">
3558 + <span class="dashicons dashicons-admin-generic"></span>
3559 + </div>
3560 + <div class="mxchat-selected-action-details">
3561 + <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
3562 + <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
3563 + </div>
3564 + </div>
3565 + </div>
3566 +
3567 + <div class="mxchat-form-group">
3568 + <label for="intent_label">
3569 + <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
3570 + </label>
3571 + <input name="intent_label" type="text" id="intent_label" required
3572 + class="mxchat-intent-input"
3573 + placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
3574 + </div>
3575 +
3576 + <div class="mxchat-form-group">
3577 + <label for="phrases">
3578 + <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
3579 + </label>
3580 + <textarea name="phrases" id="action_phrases" rows="5" required
3581 + class="mxchat-intent-textarea"
3582 + placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
3583 + </div>
3584 +
3585 + <div class="mxchat-form-group">
3586 + <label for="similarity_threshold">
3587 + <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
3588 + <span class="mxchat-threshold-value-display">85%</span>
3589 + </label>
3590 + <div class="mxchat-slider-group modal-slider">
3591 + <input type="range"
3592 + name="similarity_threshold"
3593 + id="similarity_threshold"
3594 + min="10"
3595 + max="95"
3596 + value="85"
3597 + class="mxchat-intent-slider"
3598 + oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
3599 + </div>
3600 + <div class="mxchat-threshold-hint">
3601 + <?php esc_html_e('Lower values (10-30) make the action trigger more easily. Higher values (70-95) require more exact matches.', 'mxchat'); ?>
3602 + </div>
3603 + </div>
3604 +
3605 + <!-- Bot Selection Section -->
3606 + <div class="mxchat-form-group">
3607 + <label for="enabled_bots">
3608 + <?php esc_html_e('Which bots should we enable this action for?', 'mxchat'); ?>
3609 + </label>
3610 + <div class="mxchat-bot-selector">
3611 + <div class="mxchat-bot-option">
3612 + <label class="mxchat-checkbox-label">
3613 + <input type="checkbox"
3614 + name="enabled_bots[]"
3615 + value="default"
3616 + id="bot_default"
3617 + checked="checked">
3618 + <span class="mxchat-checkmark"></span>
3619 + <?php esc_html_e('Default Bot', 'mxchat'); ?>
3620 + </label>
3621 + </div>
3622 +
3623 + <?php if (class_exists('MxChat_Multi_Bot_Core_Manager')) :
3624 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
3625 + $available_bots = $multi_bot_manager->get_available_bots();
3626 +
3627 + foreach ($available_bots as $bot_id => $bot_name) :
3628 + if ($bot_id === 'default') continue; // Skip default, already shown above
3629 + ?>
3630 + <div class="mxchat-bot-option">
3631 + <label class="mxchat-checkbox-label">
3632 + <input type="checkbox"
3633 + name="enabled_bots[]"
3634 + value="<?php echo esc_attr($bot_id); ?>"
3635 + id="bot_<?php echo esc_attr($bot_id); ?>">
3636 + <span class="mxchat-checkmark"></span>
3637 + <?php echo esc_html($bot_name); ?>
3638 + </label>
3639 + </div>
3640 + <?php
3641 + endforeach;
3642 + endif; ?>
3643 + </div>
3644 + </div>
3645 +
3646 + <div class="mxchat-modal-actions">
3647 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3648 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3649 + </button>
3650 + <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
3651 + <?php esc_html_e('Save Action', 'mxchat'); ?>
3652 + </button>
3653 + </div>
2834 3654 </div>
2835 3655 </form>
2836 3656 </div>
2837 3657 </div>
2838 3658
2839 - </div><!-- .mxchat-content -->
3659 +<div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
3660 + <div class="mxchat-action-loading-spinner"></div>
3661 + <div class="mxchat-action-loading-text">
3662 + <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
3663 + </div>
3664 +</div>
2840 3665 </div><!-- .mxchat-wrapper -->
3666 + <?php
3667 +}
3668 +private function get_trimmed_phrases($phrases, $max_length = 100) {
3669 + if (strlen($phrases) <= $max_length) {
3670 + return $phrases;
3671 + }
2841 3672
2842 - <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
2843 - <div class="mxchat-intent-loading-spinner"></div>
2844 - <div class="mxchat-intent-loading-text">
2845 - <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
2846 - </div>
2847 - </div>
2848 -<?php
3673 + $trimmed = substr($phrases, 0, $max_length);
3674 + $last_comma = strrpos($trimmed, ',');
3675 +
3676 + if ($last_comma !== false) {
3677 + $trimmed = substr($trimmed, 0, $last_comma);
3678 + }
3679 +
3680 + return $trimmed . '...';
2849 3681 }
3682 +public function mxchat_add_enabled_column_to_intents() {
3683 + global $wpdb;
3684 + $table_name = $wpdb->prefix . 'mxchat_intents';
2850 3685
3686 + // Check if the column already exists
3687 + $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
2851 3688
3689 + if (empty($columns)) {
3690 + // Add the column with default value of 1 (enabled)
3691 + $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
3692 + }
3693 +}
2852 3694
2853 -/**
2854 - * Handle editing of intent phrases
2855 - *
2856 - * @since 1.0.0
2857 - * @return void
2858 - */
2859 -public function handle_edit_intent() {
2860 - // Verify nonce and user capabilities
2861 - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
2862 - wp_die(esc_html__('Security check failed.', 'mxchat'));
3695 +public function mxchat_handle_delete_intent() {
3696 + if ( ! current_user_can( 'manage_options' ) ) {
3697 + wp_die( esc_html__('Unauthorized user', 'mxchat') );
2863 3698 }
2864 3699
3700 + check_admin_referer('mxchat_delete_intent_nonce');
3701 +
3702 + if (isset($_POST['intent_id'])) {
3703 + global $wpdb;
3704 + $table_name = $wpdb->prefix . 'mxchat_intents';
3705 + $intent_id = intval($_POST['intent_id']);
3706 +
3707 + $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3708 + }
3709 +
3710 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3711 + exit;
3712 +}
3713 +public function mxchat_handle_edit_intent() {
3714 + // Security checks (nonce and permissions)
2865 3715 if (!current_user_can('manage_options')) {
2866 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
3716 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2867 3717 }
3718 + check_admin_referer('mxchat_edit_intent');
2868 3719
2869 - // Validate and sanitize input
3720 + // Get POST data
2870 3721 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2871 - if (!$intent_id) {
2872 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
3722 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
3723 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
3724 + $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
3725 + $similarity_threshold = min(95, max(10, $threshold_percentage)) / 100; // Convert to 0.10–0.95
3726 +
3727 + // Handle enabled_bots
3728 + $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
3729 + $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
3730 +
3731 + // Ensure default is always included for backward compatibility
3732 + if (!in_array('default', $enabled_bots)) {
3733 + $enabled_bots[] = 'default';
2873 3734 }
3735 +
3736 + $enabled_bots_json = json_encode($enabled_bots);
2874 3737
2875 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2876 - if (empty($phrases_input)) {
2877 - wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
3738 + // Validate inputs
3739 + if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
3740 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
3741 + return;
2878 3742 }
2879 3743
2880 - // Process phrases the same way as in intent creation
2881 3744 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2882 -
2883 3745 if (empty($phrases_array)) {
2884 - wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
3746 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
3747 + return;
2885 3748 }
2886 3749
2887 - // Generate embeddings and combine them
3750 + // Generate embeddings with improved error handling
2888 3751 $vectors = [];
3752 + $failed_phrases = [];
3753 +
2889 3754 foreach ($phrases_array as $phrase) {
2890 3755 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2891 3756 if (is_array($embedding_vector)) {
2892 3757 $vectors[] = $embedding_vector;
2893 3758 } else {
2894 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
3759 + $failed_phrases[] = $phrase;
2895 3760 }
2896 3761 }
2897 3762
3763 + if (!empty($failed_phrases)) {
3764 + $this->handle_embedding_error(
3765 + sprintf(
3766 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
3767 + implode(', ', $failed_phrases)
3768 + )
3769 + );
3770 + return;
3771 + }
3772 +
2898 3773 if (empty($vectors)) {
2899 - wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3774 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3775 + return;
2900 3776 }
2901 3777
2902 - // Create combined vector just like in intent creation
2903 3778 $combined_vector = $this->mxchat_average_vectors($vectors);
2904 3779 $serialized_vector = maybe_serialize($combined_vector);
2905 3780
3781 + // Update the database
2906 3782 global $wpdb;
2907 3783 $table_name = $wpdb->prefix . 'mxchat_intents';
2908 3784
2909 - // Update the intent with both new phrases and the combined vector
2910 3785 $result = $wpdb->update(
2911 3786 $table_name,
2912 3787 array(
3788 + 'intent_label' => $intent_label,
2913 3789 'phrases' => implode(', ', $phrases_array),
2914 - 'embedding_vector' => $serialized_vector
3790 + 'embedding_vector' => $serialized_vector,
3791 + 'similarity_threshold' => $similarity_threshold,
3792 + 'enabled_bots' => $enabled_bots_json // Include enabled_bots in update
2915 3793 ),
2916 3794 array('id' => $intent_id),
2917 - array('%s', '%s'),
2918 - array('%d')
3795 + array('%s', '%s', '%s', '%f', '%s'), // Format: string, string, string, float, string
3796 + array('%d') // Where format: integer
2919 3797 );
2920 3798
2921 3799 if (false === $result) {
2922 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
3800 + $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
3801 + return;
2923 3802 }
2924 3803
2925 - // Redirect back to the intents page with a success message
3804 + // Set success message and redirect
3805 + set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
3806 +
2926 3807 $redirect_url = add_query_arg(
2927 3808 array(
2928 - 'page' => 'mxchat-intents',
2929 - 'updated' => 'true'
3809 + 'page' => 'mxchat-actions'
2930 3810 ),
2931 3811 admin_url('admin.php')
2932 3812 );
2933 -
2934 3813 wp_safe_redirect($redirect_url);
2935 3814 exit;
2936 3815 }
2937 -public function mxchat_handle_update_intent_threshold() {
2938 - if ( ! current_user_can( 'manage_options' ) ) {
2939 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
2940 - }
2941 -
2942 - check_admin_referer('mxchat_update_intent_threshold_nonce');
2943 -
2944 - if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
2945 - global $wpdb;
2946 - $table_name = $wpdb->prefix . 'mxchat_intents';
2947 - $intent_id = intval($_POST['intent_id']);
2948 - $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
2949 - $similarity_threshold = $threshold_percentage / 100;
2950 -
2951 - $wpdb->update(
2952 - $table_name,
2953 - ['similarity_threshold' => $similarity_threshold],
2954 - ['id' => $intent_id],
2955 - ['%f'],
2956 - ['%d']
2957 - );
2958 - }
2959 -
2960 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2961 - exit;
2962 -}
2963 -
2964 3816 public function mxchat_handle_add_intent() {
2965 - if ( ! current_user_can( 'manage_options' ) ) {
2966 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3817 + if (!current_user_can('manage_options')) {
3818 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2967 3819 }
2968 -
2969 3820 check_admin_referer('mxchat_add_intent_nonce');
2970 -
2971 3821 global $wpdb;
2972 3822 $table_name = $wpdb->prefix . 'mxchat_intents';
2973 -
3823 +
3824 + // Sanitize and get form data
2974 3825 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
2975 3826 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2976 3827 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2977 - $default_threshold = 0.85;
2978 -
3828 +
3829 + // Get similarity threshold from form (convert percentage to decimal)
3830 + $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
3831 +
3832 + // Handle enabled_bots
3833 + $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
3834 + $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
3835 +
3836 + // Ensure default is always included for backward compatibility with existing actions
3837 + if (!in_array('default', $enabled_bots)) {
3838 + $enabled_bots[] = 'default';
3839 + }
3840 +
3841 + $enabled_bots_json = json_encode($enabled_bots);
3842 +
3843 + // Validate required fields
2979 3844 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
2980 - wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
3845 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
3846 + return;
2981 3847 }
2982 -
3848 +
3849 + // Validate callback function
2983 3850 $available_callbacks = $this->mxchat_get_available_callbacks();
2984 -
2985 3851 if (!array_key_exists($callback_function, $available_callbacks)) {
2986 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
3852 + $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
3853 + return;
2987 3854 }
2988 -
3855 +
3856 + // Check Pro requirements
2989 3857 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2990 3858 if ($is_pro_only && !$this->is_activated) {
2991 - wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
3859 + $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
3860 + return;
2992 3861 }
2993 -
3862 +
3863 + // Process phrases
2994 3864 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2995 -
2996 3865 if (empty($phrases_array)) {
2997 - wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
3866 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
3867 + return;
2998 3868 }
2999 -
3869 +
3870 + // Generate embeddings with improved error handling
3000 3871 $vectors = [];
3872 + $failed_phrases = [];
3001 3873 foreach ($phrases_array as $phrase) {
3002 3874 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3003 3875 if (is_array($embedding_vector)) {
3004 3876 $vectors[] = $embedding_vector;
3005 3877 } else {
3006 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
3878 + $failed_phrases[] = $phrase;
3007 3879 }
3008 3880 }
3881 +
3882 + // Check for embedding failures
3883 + if (!empty($failed_phrases)) {
3884 + $this->handle_embedding_error(
3885 + sprintf(
3886 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
3887 + implode(', ', $failed_phrases)
3888 + )
3889 + );
3890 + return;
3891 + }
3892 +
3893 + if (empty($vectors)) {
3894 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3895 + return;
3896 + }
3897 +
3898 + // Create combined vector and insert into database
3899 + $combined_vector = $this->mxchat_average_vectors($vectors);
3900 + $serialized_vector = maybe_serialize($combined_vector);
3901 +
3902 + $result = $wpdb->insert($table_name, [
3903 + 'intent_label' => $intent_label,
3904 + 'phrases' => implode(', ', $phrases_array),
3905 + 'embedding_vector' => $serialized_vector,
3906 + 'callback_function' => $callback_function,
3907 + 'similarity_threshold' => $similarity_threshold,
3908 + 'enabled_bots' => $enabled_bots_json // NEW field
3909 + ]);
3910 +
3911 + if ($result === false) {
3912 + $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
3913 + return;
3914 + }
3915 +
3916 + // Set success message and redirect
3917 + set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
3918 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3919 + exit;
3920 +}
3009 3921
3010 - if (!empty($vectors)) {
3011 - $combined_vector = $this->mxchat_average_vectors($vectors);
3012 - $serialized_vector = maybe_serialize($combined_vector);
3013 3922
3014 - $result = $wpdb->insert($table_name, [
3015 - 'intent_label' => $intent_label,
3016 - 'phrases' => implode(', ', $phrases_array),
3017 - 'embedding_vector' => $serialized_vector,
3018 - 'callback_function' => $callback_function,
3019 - 'similarity_threshold' => $default_threshold,
3020 - ]);
3021 3923
3022 - if ($result === false) {
3023 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3924 +
3925 +private function handle_embedding_error($message, $redirect = true) {
3926 + // Store the error message in the existing transient
3927 + set_transient('mxchat_admin_notice_error', $message, 60);
3928 +
3929 + if ($redirect) {
3930 + // Redirect back to the actions page
3931 + $redirect_url = add_query_arg(
3932 + array(
3933 + 'page' => 'mxchat-actions'
3934 + ),
3935 + admin_url('admin.php')
3936 + );
3937 + wp_safe_redirect($redirect_url);
3938 + exit;
3939 + }
3940 +}
3941 +
3942 +
3943 +/**
3944 + * Enhanced get_available_callbacks function with form action exclusion
3945 + *
3946 + * @param bool $grouped Whether to return callbacks grouped by category
3947 + * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
3948 + * @return array Callbacks data with icons, descriptions and availability status
3949 + */
3950 +private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
3951 + // Load WordPress plugin functions if needed
3952 + if (!function_exists('get_plugins')) {
3953 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
3954 + }
3955 +
3956 + // Get active plugins
3957 + $active_plugins = get_option('active_plugins', array());
3958 +
3959 + // Functions to exclude from the action selector only if Pro is activated
3960 + // If user doesn't have Pro, show these so they can see what they're missing
3961 + $excluded_when_pro_active_functions = array(
3962 + 'mxchat_handle_form_collection' // Forms add-on action
3963 + );
3964 +
3965 + // Always excluded functions (regardless of Pro status)
3966 + $always_excluded_functions = array();
3967 +
3968 + // Combine exclusion lists based on Pro activation status
3969 + $excluded_functions = $always_excluded_functions;
3970 + if ($this->is_activated) {
3971 + // Only exclude add-on managed functions if Pro is active
3972 + $excluded_functions = array_merge($excluded_functions, $excluded_when_pro_active_functions);
3973 + }
3974 +
3975 + // Define add-on plugin files and their corresponding action functions
3976 + $addon_plugins = array(
3977 + 'mxchat-woo/mxchat-woo.php' => array(
3978 + 'functions' => array(
3979 + 'mxchat_handle_product_recommendations',
3980 + 'mxchat_handle_order_history',
3981 + 'mxchat_show_product_card',
3982 + 'mxchat_add_to_cart',
3983 + 'mxchat_checkout_redirect'
3984 + ),
3985 + 'name' => __('WooCommerce Add-on', 'mxchat'),
3986 + 'pro_required' => true
3987 + ),
3988 + 'mxchat-perplexity/mxchat-perplexity.php' => array(
3989 + 'functions' => array('mxchat_perplexity_research'),
3990 + 'name' => __('Perplexity Add-on', 'mxchat'),
3991 + 'pro_required' => true
3992 + ),
3993 + 'mxchat-forms/mxchat-forms.php' => array(
3994 + 'functions' => array('mxchat_handle_form_collection'),
3995 + 'name' => __('Forms Add-on', 'mxchat'),
3996 + 'pro_required' => true
3997 + ),
3998 + // Add other add-ons and their functions here
3999 + );
4000 +
4001 + // Get the functions that are provided by active add-ons
4002 + $addon_provided_functions = array();
4003 + $addon_function_mapping = array(); // Maps functions to their add-on info
4004 +
4005 + // Check which add-ons are active
4006 + foreach ($addon_plugins as $plugin_file => $addon_info) {
4007 + $is_active = in_array($plugin_file, $active_plugins);
4008 +
4009 + // For each function in this addon
4010 + foreach ($addon_info['functions'] as $function) {
4011 + // Consider a function installed only if:
4012 + // 1. The add-on is active AND
4013 + // 2. Either it doesn't require Pro OR Pro is activated
4014 + $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
4015 +
4016 + // If the add-on is installed, mark this function as provided by an add-on
4017 + if ($is_installed) {
4018 + $addon_provided_functions[] = $function;
4019 + }
4020 +
4021 + // Store addon info for this function regardless of installation status
4022 + $addon_function_mapping[$function] = array(
4023 + 'addon' => basename(dirname($plugin_file)),
4024 + 'addon_name' => $addon_info['name'],
4025 + 'pro_required' => $addon_info['pro_required'],
4026 + 'is_active' => $is_active,
4027 + 'is_installed' => $is_installed
4028 + );
3024 4029 }
3025 - } else {
3026 - wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
3027 4030 }
3028 4031
3029 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3030 - exit;
3031 -}
4032 + // Core callbacks - always available in the base plugin
4033 + $core_callbacks = array(
4034 + 'mxchat_handle_email_capture' => array(
4035 + 'label' => __('Loops Email Capture', 'mxchat'),
4036 + 'pro_only' => false,
4037 + 'group' => __('Customer Engagement', 'mxchat'),
4038 + 'icon' => 'email-alt',
4039 + 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
4040 + 'addon' => false, // Not from an add-on
4041 + 'installed' => true // Always installed with base plugin
4042 + ),
4043 + 'mxchat_handle_search_request' => array(
4044 + 'label' => __('Brave Web Search', 'mxchat'),
4045 + 'pro_only' => false,
4046 + 'group' => __('Search Features', 'mxchat'),
4047 + 'icon' => 'search',
4048 + 'description' => __('Let users search the web directly from the chat', 'mxchat'),
4049 + 'addon' => false,
4050 + 'installed' => true
4051 + ),
4052 + 'mxchat_handle_image_search_request' => array(
4053 + 'label' => __('Brave Image Search', 'mxchat'),
4054 + 'pro_only' => false,
4055 + 'group' => __('Search Features', 'mxchat'),
4056 + 'icon' => 'format-image',
4057 + 'description' => __('Search and display images in the chat conversation', 'mxchat'),
4058 + 'addon' => false,
4059 + 'installed' => true
4060 + ),
4061 + // Pro core features - check is_activated property
4062 + 'mxchat_generate_image' => array(
4063 + 'label' => __('Generate Image', 'mxchat'),
4064 + 'pro_only' => false,
4065 + 'group' => __('Other Features', 'mxchat'),
4066 + 'icon' => 'art',
4067 + 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
4068 + 'addon' => false,
4069 + 'installed' => true
4070 + ),
4071 + 'mxchat_handle_pdf_discussion' => array(
4072 + 'label' => __('Chat with PDF', 'mxchat'),
4073 + 'pro_only' => false,
4074 + 'group' => __('Other Features', 'mxchat'),
4075 + 'icon' => 'media-document',
4076 + 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
4077 + 'addon' => false,
4078 + 'installed' => true
4079 + ),
4080 + 'mxchat_live_agent_handover' => array(
4081 + 'label' => __('Slack Live Agent', 'mxchat'),
4082 + 'pro_only' => false,
4083 + 'group' => __('Customer Engagement', 'mxchat'),
4084 + 'icon' => 'admin-users',
4085 + 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
4086 + 'addon' => false,
4087 + 'installed' => true
4088 + ),
4089 + 'mxchat_handle_switch_to_chatbot_intent' => array(
4090 + 'label' => __('Back to Chatbot', 'mxchat'),
4091 + 'pro_only' => false,
4092 + 'group' => __('Customer Engagement', 'mxchat'),
4093 + 'icon' => 'backup',
4094 + 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
4095 + 'addon' => false,
4096 + 'installed' => true
4097 + ),
4098 + );
3032 4099
4100 + // Add-on callbacks with placeholders - only include if the add-on is NOT active
4101 + $addon_callbacks = array(
4102 + // WooCommerce Add-on
4103 + 'mxchat_handle_product_recommendations' => array(
4104 + 'label' => __('Product Recommendations', 'mxchat'),
4105 + 'pro_only' => true,
4106 + 'group' => __('WooCommerce Features', 'mxchat'),
4107 + 'icon' => 'cart',
4108 + 'description' => __('Suggest products based on customer preferences', 'mxchat'),
4109 + ),
4110 + 'mxchat_handle_order_history' => array(
4111 + 'label' => __('Order History', 'mxchat'),
4112 + 'pro_only' => true,
4113 + 'group' => __('WooCommerce Features', 'mxchat'),
4114 + 'icon' => 'clipboard',
4115 + 'description' => __('Allow customers to check their order status', 'mxchat'),
4116 + ),
4117 + 'mxchat_show_product_card' => array(
4118 + 'label' => __('Show Product Card', 'mxchat'),
4119 + 'pro_only' => true,
4120 + 'group' => __('WooCommerce Features', 'mxchat'),
4121 + 'icon' => 'products',
4122 + 'description' => __('Display product information in the chat', 'mxchat'),
4123 + ),
4124 + 'mxchat_add_to_cart' => array(
4125 + 'label' => __('Add to Cart', 'mxchat'),
4126 + 'pro_only' => true,
4127 + 'group' => __('WooCommerce Features', 'mxchat'),
4128 + 'icon' => 'plus-alt',
4129 + 'description' => __('Add products to cart directly from chat', 'mxchat'),
4130 + ),
4131 + 'mxchat_checkout_redirect' => array(
4132 + 'label' => __('Proceed to Checkout', 'mxchat'),
4133 + 'pro_only' => true,
4134 + 'group' => __('WooCommerce Features', 'mxchat'),
4135 + 'icon' => 'arrow-right-alt',
4136 + 'description' => __('Redirect customer to checkout page', 'mxchat'),
4137 + ),
3033 4138
4139 + // Perplexity Add-on
4140 + 'mxchat_perplexity_research' => array(
4141 + 'label' => __('Perplexity Research', 'mxchat'),
4142 + 'pro_only' => true,
4143 + 'group' => __('Search Features', 'mxchat'),
4144 + 'icon' => 'book-alt',
4145 + 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
4146 + ),
3034 4147
4148 + // Forms Add-on (only shown when Pro is not activated)
4149 + 'mxchat_handle_form_collection' => array(
4150 + 'label' => __('Form Collection', 'mxchat'),
4151 + 'pro_only' => true,
4152 + 'group' => __('Form Features', 'mxchat'),
4153 + 'icon' => 'feedback',
4154 + 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
4155 + ),
4156 + );
3035 4157
4158 + // Enhance add-on callbacks with installation status and addon info
4159 + foreach ($addon_callbacks as $function => $data) {
4160 + if (isset($addon_function_mapping[$function])) {
4161 + $addon_info = $addon_function_mapping[$function];
3036 4162
4163 + $addon_callbacks[$function]['addon'] = $addon_info['addon'];
4164 + $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4165 + $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
3037 4166
3038 -private function mxchat_get_available_callbacks($grouped = false) {
3039 - $callbacks = [
3040 - 'mxchat_handle_email_capture' => [
3041 - 'label' => __('Email Capture', 'mxchat'),
3042 - 'pro_only' => false,
3043 - 'group' => __('Customer Engagement', 'mxchat'),
3044 - ],
3045 - 'mxchat_handle_product_inquiry' => [
3046 - 'label' => __('Show Product Card', 'mxchat'),
3047 - 'pro_only' => true,
3048 - 'group' => __('WooCommerce Features', 'mxchat'),
3049 - ],
3050 - 'mxchat_handle_order_history' => [
3051 - 'label' => __('Order History', 'mxchat'),
3052 - 'pro_only' => true,
3053 - 'group' => __('WooCommerce Features', 'mxchat'),
3054 - ],
3055 - 'mxchat_generate_image' => [
3056 - 'label' => __('Generate Image', 'mxchat'),
3057 - 'pro_only' => true,
3058 - 'group' => __('Other Features', 'mxchat'),
3059 - ],
3060 - 'mxchat_handle_search_request' => [
3061 - 'label' => __('Brave Web Search', 'mxchat'),
3062 - 'pro_only' => false,
3063 - 'group' => __('Search Features', 'mxchat'),
3064 - ],
3065 - 'mxchat_handle_image_search_request' => [
3066 - 'label' => __('Brave Image Search', 'mxchat'),
3067 - 'pro_only' => false,
3068 - 'group' => __('Search Features', 'mxchat'),
3069 - ],
3070 - 'mxchat_handle_add_to_cart_intent' => [
3071 - 'label' => __('Add to Cart', 'mxchat'),
3072 - 'pro_only' => true,
3073 - 'group' => __('WooCommerce Features', 'mxchat'),
3074 - ],
3075 - 'mxchat_handle_checkout_intent' => [
3076 - 'label' => __('Proceed to Checkout', 'mxchat'),
3077 - 'pro_only' => true,
3078 - 'group' => __('WooCommerce Features', 'mxchat'),
3079 - ],
3080 - 'mxchat_handle_pdf_discussion' => [
3081 - 'label' => __('Chat with PDF', 'mxchat'),
3082 - 'pro_only' => true,
3083 - 'group' => __('Other Features', 'mxchat'),
3084 - ],
3085 - 'mxchat_handle_product_recommendations' => [
3086 - 'label' => __('Product Recommendations', 'mxchat'),
3087 - 'pro_only' => true,
3088 - 'group' => __('WooCommerce Features', 'mxchat'),
3089 - ],
3090 - 'mxchat_live_agent_handover' => [
3091 - 'label' => __('Live Agent', 'mxchat'),
3092 - 'pro_only' => true,
3093 - 'group' => __('Customer Engagement', 'mxchat'),
3094 - ],
3095 - 'mxchat_handle_switch_to_chatbot_intent' => [
3096 - 'label' => __('Back to Chatbot', 'mxchat'),
3097 - 'pro_only' => true,
3098 - 'group' => __('Customer Engagement', 'mxchat'),
3099 - ],
3100 - ];
3101 -
3102 - // Return grouped structure if requested
3103 - if ($grouped) {
3104 - $grouped_callbacks = [];
3105 - foreach ($callbacks as $key => $data) {
3106 - $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3107 - $grouped_callbacks[$group_label][$key] = [
3108 - 'label' => $data['label'],
3109 - 'pro_only' => $data['pro_only'],
3110 - ];
4167 + // Set pro_only based on add-on configuration
4168 + $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4169 + } else {
4170 + $addon_callbacks[$function]['addon'] = 'unknown';
4171 + $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
4172 + $addon_callbacks[$function]['installed'] = false;
3111 4173 }
3112 - return $grouped_callbacks;
3113 4174 }
3114 4175
3115 - return $callbacks;
3116 -}
4176 + // Initialize callbacks with core features
4177 + $callbacks = $core_callbacks;
3117 4178
4179 + // Get callbacks from active add-ons
4180 + $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
3118 4181
4182 + // Add placeholder callbacks only for add-ons that aren't active
4183 + if ($include_all) {
4184 + foreach ($addon_callbacks as $function => $data) {
4185 + // Skip placeholders for functions provided by active add-ons
4186 + if (in_array($function, $addon_provided_functions)) {
4187 + continue;
4188 + }
3119 4189
3120 -private function mxchat_average_vectors($vectors) {
3121 - $vector_length = count($vectors[0]);
3122 - $sum_vector = array_fill(0, $vector_length, 0);
4190 + // Skip excluded functions
4191 + if (in_array($function, $excluded_functions)) {
4192 + continue;
4193 + }
3123 4194
3124 - foreach ($vectors as $vector) {
3125 - for ($i = 0; $i < $vector_length; $i++) {
3126 - $sum_vector[$i] += $vector[$i];
4195 + // Add the placeholder
4196 + $callbacks[$function] = $data;
3127 4197 }
3128 4198 }
3129 4199
3130 - // Divide each component by the number of vectors to get the average
3131 - $num_vectors = count($vectors);
3132 - for ($i = 0; $i < $vector_length; $i++) {
3133 - $sum_vector[$i] /= $num_vectors;
4200 + // Add callbacks from active add-ons (will override placeholders)
4201 + foreach ($active_addon_callbacks as $function => $data) {
4202 + // Skip excluded functions
4203 + if (in_array($function, $excluded_functions)) {
4204 + continue;
4205 + }
4206 +
4207 + // Always include callbacks from add-ons
4208 + $callbacks[$function] = $data;
4209 +
4210 + // Ensure they have the proper add-on info
4211 + if (isset($addon_function_mapping[$function])) {
4212 + $addon_info = $addon_function_mapping[$function];
4213 + $callbacks[$function]['addon'] = $addon_info['addon'];
4214 + $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4215 + $callbacks[$function]['installed'] = $addon_info['is_installed'];
4216 + $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4217 + }
3134 4218 }
3135 4219
3136 - return $sum_vector;
3137 -}
4220 + // Just before returning callbacks, sort them to prioritize free features
4221 + if (!$grouped) {
4222 + // Create temporary arrays for sorting
4223 + $free_callbacks = array();
4224 + $pro_callbacks = array();
3138 4225
3139 -public function mxchat_handle_delete_intent() {
3140 - if ( ! current_user_can( 'manage_options' ) ) {
3141 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
4226 + // Split callbacks into free and pro
4227 + foreach ($callbacks as $key => $data) {
4228 + if (isset($data['pro_only']) && $data['pro_only']) {
4229 + $pro_callbacks[$key] = $data;
4230 + } else {
4231 + $free_callbacks[$key] = $data;
4232 + }
4233 + }
4234 +
4235 + // Merge with free callbacks first
4236 + $callbacks = array_merge($free_callbacks, $pro_callbacks);
3142 4237 }
3143 4238
3144 - check_admin_referer('mxchat_delete_intent_nonce');
4239 + // Return grouped structure if requested
4240 + if ($grouped) {
4241 + $grouped_callbacks = array();
4242 + foreach ($callbacks as $key => $data) {
4243 + $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
3145 4244
3146 - if (isset($_POST['intent_id'])) {
3147 - global $wpdb;
3148 - $table_name = $wpdb->prefix . 'mxchat_intents';
3149 - $intent_id = intval($_POST['intent_id']);
4245 + // Ensure we carry forward all the new fields in grouped mode
4246 + $callback_data = array(
4247 + 'label' => $data['label'],
4248 + 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
4249 + 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
4250 + 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
4251 + 'addon' => isset($data['addon']) ? $data['addon'] : false,
4252 + 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
4253 + 'installed' => isset($data['installed']) ? $data['installed'] : true
4254 + );
3150 4255
3151 - $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
4256 + $grouped_callbacks[$group_label][$key] = $callback_data;
4257 + }
4258 +
4259 + // Sort within each group to prioritize free features
4260 + foreach ($grouped_callbacks as $group => $items) {
4261 + $free_items = array();
4262 + $pro_items = array();
4263 +
4264 + foreach ($items as $key => $data) {
4265 + if (isset($data['pro_only']) && $data['pro_only']) {
4266 + $pro_items[$key] = $data;
4267 + } else {
4268 + $free_items[$key] = $data;
4269 + }
4270 + }
4271 +
4272 + $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
4273 + }
4274 +
4275 + return $grouped_callbacks;
3152 4276 }
3153 4277
3154 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3155 - exit;
4278 + return $callbacks;
3156 4279 }
3157 4280
3158 -
3159 4281 public function mxchat_page_init() {
3160 - // Register settings
3161 4282 register_setting(
3162 4283 'mxchat_option_group',
3163 4284 'mxchat_options',
3164 4285 array($this, 'mxchat_sanitize')
@@ -3169,10 +4290,10 @@
3169 4290 'mxchat_similarity_threshold',
3170 4291 array(
3171 4292 'type' => 'number',
3172 4293 'sanitize_callback' => function($value) {
3173 - $value = absint($value); // Ensure it's an integer
3174 - return min(max($value, 70), 85); // Enforce range
4294 + $value = absint($value);
4295 + return min(max($value, 20), 95);
3175 4296 },
3176 4297 'default' => 80,
3177 4298 )
3178 4299 );
@@ -3184,8 +4305,97 @@
3184 4305 null,
3185 4306 'mxchat-chatbot'
3186 4307 );
3187 4308
4309 + // API Keys Settings Section
4310 + add_settings_section(
4311 + 'mxchat_api_keys_section',
4312 + esc_html__('API Keys', 'mxchat'),
4313 + array($this, 'mxchat_api_keys_section_callback'),
4314 + 'mxchat-api-keys'
4315 + );
4316 +
4317 + // OpenAI API Key
4318 + add_settings_field(
4319 + 'api_key',
4320 + esc_html__('OpenAI API Key', 'mxchat'),
4321 + array($this, 'api_key_callback'),
4322 + 'mxchat-api-keys',
4323 + 'mxchat_api_keys_section'
4324 + );
4325 +
4326 + // X.AI API Key
4327 + add_settings_field(
4328 + 'xai_api_key',
4329 + esc_html__('X.AI API Key', 'mxchat'),
4330 + array($this, 'xai_api_key_callback'),
4331 + 'mxchat-api-keys',
4332 + 'mxchat_api_keys_section'
4333 + );
4334 +
4335 + // Claude API Key
4336 + add_settings_field(
4337 + 'claude_api_key',
4338 + esc_html__('Claude API Key', 'mxchat'),
4339 + array($this, 'claude_api_key_callback'),
4340 + 'mxchat-api-keys',
4341 + 'mxchat_api_keys_section'
4342 + );
4343 +
4344 + // DeepSeek API Key
4345 + add_settings_field(
4346 + 'deepseek_api_key',
4347 + esc_html__('DeepSeek API Key', 'mxchat'),
4348 + array($this, 'deepseek_api_key_callback'),
4349 + 'mxchat-api-keys',
4350 + 'mxchat_api_keys_section'
4351 + );
4352 +
4353 + // Google Gemini API Key
4354 + add_settings_field(
4355 + 'gemini_api_key',
4356 + esc_html__('Google Gemini API Key', 'mxchat'),
4357 + array($this, 'gemini_api_key_callback'),
4358 + 'mxchat-api-keys',
4359 + 'mxchat_api_keys_section'
4360 + );
4361 +
4362 + // Voyage AI API Key
4363 + add_settings_field(
4364 + 'voyage_api_key',
4365 + esc_html__('Voyage AI API Key', 'mxchat'),
4366 + array($this, 'voyage_api_key_callback'),
4367 + 'mxchat-api-keys',
4368 + 'mxchat_api_keys_section'
4369 + );
4370 +
4371 + // OpenRouter API Key
4372 + add_settings_field(
4373 + 'openrouter_api_key',
4374 + esc_html__('OpenRouter API Key', 'mxchat'),
4375 + array($this, 'openrouter_api_key_callback'),
4376 + 'mxchat-api-keys',
4377 + 'mxchat_api_keys_section'
4378 + );
4379 +
4380 + // Loops API Key
4381 + add_settings_field(
4382 + 'loops_api_key',
4383 + esc_html__('Loops API Key', 'mxchat'),
4384 + array($this, 'mxchat_loops_api_key_callback'),
4385 + 'mxchat-api-keys',
4386 + 'mxchat_api_keys_section'
4387 + );
4388 +
4389 + // Brave Search API Key
4390 + add_settings_field(
4391 + 'brave_api_key',
4392 + __('Brave API Key', 'mxchat'),
4393 + array($this, 'mxchat_brave_api_key_callback'),
4394 + 'mxchat-api-keys',
4395 + 'mxchat_api_keys_section'
4396 + );
4397 +
3188 4398 // Similarity Threshold Slider
3189 4399 add_settings_field(
3190 4400 'similarity_threshold', // Field ID
3191 4401 esc_html__('Similarity Threshold', 'mxchat'), // Field title
@@ -3201,38 +4411,41 @@
3201 4411 'mxchat-chatbot',
3202 4412 'mxchat_chatbot_section'
3203 4413 );
3204 4414
3205 -
3206 - // Existing fields...
3207 4415 add_settings_field(
3208 - 'api_key',
3209 - esc_html__('OpenAI API Key', 'mxchat'),
3210 - array($this, 'api_key_callback'),
4416 + 'contextual_awareness_toggle',
4417 + esc_html__('Contextual Awareness', 'mxchat'),
4418 + array($this, 'mxchat_contextual_awareness_callback'),
3211 4419 'mxchat-chatbot',
3212 4420 'mxchat_chatbot_section'
3213 4421 );
3214 4422
3215 4423 add_settings_field(
3216 - 'xai_api_key',
3217 - esc_html__('X.AI API Key', 'mxchat'),
3218 - array($this, 'xai_api_key_callback'),
4424 + 'enable_streaming_toggle',
4425 + esc_html__('Enable Streaming', 'mxchat'),
4426 + array($this, 'enable_streaming_toggle_callback'),
3219 4427 'mxchat-chatbot',
3220 - 'mxchat_chatbot_section'
4428 + 'mxchat_chatbot_section', // Same section as your working toggle
4429 + array(
4430 + 'class' => 'mxchat-setting-row streaming-setting',
4431 + 'style' => 'display: none;' // Hidden by default, shown when OpenAI/Claude selected
4432 + )
3221 4433 );
3222 4434
4435 +
3223 4436 add_settings_field(
3224 - 'claude_api_key',
3225 - esc_html__('Claude API Key', 'mxchat'),
3226 - array($this, 'claude_api_key_callback'),
4437 + 'model',
4438 + esc_html__('Chat Model', 'mxchat'),
4439 + array($this, 'mxchat_model_callback'),
3227 4440 'mxchat-chatbot',
3228 4441 'mxchat_chatbot_section'
3229 4442 );
3230 4443
3231 4444 add_settings_field(
3232 - 'deepseek_api_key',
3233 - esc_html__('DeepSeek API Key', 'mxchat'),
3234 - array($this, 'deepseek_api_key_callback'),
4445 + 'embedding_model',
4446 + esc_html__('Embedding Model', 'mxchat'),
4447 + array($this, 'embedding_model_callback'),
3235 4448 'mxchat-chatbot',
3236 4449 'mxchat_chatbot_section'
3237 4450 );
3238 4451
@@ -3243,20 +4456,21 @@
3243 4456 'mxchat-chatbot',
3244 4457 'mxchat_chatbot_section'
3245 4458 );
3246 4459
4460 +
3247 4461 add_settings_field(
3248 - 'model',
3249 - esc_html__('Model', 'mxchat'),
3250 - array($this, 'mxchat_model_callback'),
4462 + 'top_bar_title',
4463 + esc_html__('Top Bar Title', 'mxchat'),
4464 + array($this, 'mxchat_top_bar_title_callback'),
3251 4465 'mxchat-chatbot',
3252 4466 'mxchat_chatbot_section'
3253 4467 );
3254 4468
3255 4469 add_settings_field(
3256 - 'top_bar_title',
3257 - esc_html__('Top Bar Title', 'mxchat'),
3258 - array($this, 'mxchat_top_bar_title_callback'),
4470 + 'ai_agent_text',
4471 + esc_html__('AI Agent Text', 'mxchat'),
4472 + array($this, 'mxchat_ai_agent_text_callback'),
3259 4473 'mxchat-chatbot',
3260 4474 'mxchat_chatbot_section'
3261 4475 );
3262 4476
@@ -3284,48 +4498,40 @@
3284 4498 'mxchat_chatbot_section'
3285 4499 );
3286 4500
3287 4501 add_settings_field(
3288 - 'intro_message',
3289 - esc_html__('Introductory Message', 'mxchat'),
3290 - array($this, 'mxchat_intro_message_callback'),
4502 + 'enable_name_field',
4503 + esc_html__('Require Name Field', 'mxchat'),
4504 + array($this, 'enable_name_field_callback'),
3291 4505 'mxchat-chatbot',
3292 4506 'mxchat_chatbot_section'
3293 4507 );
3294 4508
3295 4509 add_settings_field(
3296 - 'input_copy',
3297 - esc_html__('Input Copy', 'mxchat'),
3298 - array($this, 'mxchat_input_copy_callback'),
4510 + 'name_field_placeholder',
4511 + esc_html__('Name Field Placeholder', 'mxchat'),
4512 + array($this, 'name_field_placeholder_callback'),
3299 4513 'mxchat-chatbot',
3300 4514 'mxchat_chatbot_section'
3301 4515 );
3302 4516
3303 4517 add_settings_field(
3304 - 'rate_limit_logged_out',
3305 - __('Rate Limit for Logged-out Users', 'mxchat'),
3306 - array($this, 'mxchat_rate_limit_logged_out_callback'),
4518 + 'intro_message',
4519 + esc_html__('Introductory Message', 'mxchat'),
4520 + array($this, 'mxchat_intro_message_callback'),
3307 4521 'mxchat-chatbot',
3308 4522 'mxchat_chatbot_section'
3309 4523 );
3310 4524
3311 4525 add_settings_field(
3312 - 'rate_limit_roles',
3313 - __('Rate Limits by User Role', 'mxchat'),
3314 - array($this, 'mxchat_rate_limit_roles_callback'),
4526 + 'input_copy',
4527 + esc_html__('Input Copy', 'mxchat'),
4528 + array($this, 'mxchat_input_copy_callback'),
3315 4529 'mxchat-chatbot',
3316 4530 'mxchat_chatbot_section'
3317 4531 );
3318 4532
3319 4533 add_settings_field(
3320 - 'rate_limit_message',
3321 - esc_html__('Rate Limit Message', 'mxchat'),
3322 - array($this, 'mxchat_rate_limit_message_callback'),
3323 - 'mxchat-chatbot',
3324 - 'mxchat_chatbot_section'
3325 - );
3326 -
3327 - add_settings_field(
3328 4534 'pre_chat_message',
3329 4535 esc_html__('Chat Teaser Pop-up', 'mxchat'),
3330 4536 array($this, 'mxchat_pre_chat_message_callback'),
3331 4537 'mxchat-chatbot',
@@ -3365,9 +4571,9 @@
3365 4571 );
3366 4572
3367 4573 add_settings_field(
3368 4574 'popular_question_1',
3369 - esc_html__('Popular Question 1', 'mxchat'),
4575 + esc_html__('Quick Question 1', 'mxchat'),
3370 4576 array($this, 'mxchat_popular_question_1_callback'),
3371 4577 'mxchat-chatbot',
3372 4578 'mxchat_chatbot_section'
3373 4579 );
@@ -3373,9 +4579,9 @@
3373 4579 );
3374 4580
3375 4581 add_settings_field(
3376 4582 'popular_question_2',
3377 - esc_html__('Popular Question 2', 'mxchat'),
4583 + esc_html__('Quick Question 2', 'mxchat'),
3378 4584 array($this, 'mxchat_popular_question_2_callback'),
3379 4585 'mxchat-chatbot',
3380 4586 'mxchat_chatbot_section'
3381 4587 );
@@ -3381,9 +4587,9 @@
3381 4587 );
3382 4588
3383 4589 add_settings_field(
3384 4590 'popular_question_3',
3385 - esc_html__('Popular Question 3', 'mxchat'),
4591 + esc_html__('Quick Question 3', 'mxchat'),
3386 4592 array($this, 'mxchat_popular_question_3_callback'),
3387 4593 'mxchat-chatbot',
3388 4594 'mxchat_chatbot_section'
3389 4595 );
@@ -3389,20 +4595,21 @@
3389 4595 );
3390 4596
3391 4597 add_settings_field(
3392 4598 'additional_popular_questions',
3393 - esc_html__('Additional Popular Questions', 'mxchat'),
4599 + esc_html__('Additional Quick Questions', 'mxchat'),
3394 4600 array($this, 'mxchat_additional_popular_questions_callback'),
3395 4601 'mxchat-chatbot',
3396 4602 'mxchat_chatbot_section'
3397 4603 );
3398 4604
3399 - // WooCommerce Settings Section
3400 - add_settings_section(
3401 - 'mxchat_woocommerce_section',
3402 - esc_html__('WooCommerce Settings', 'mxchat'),
3403 - null,
3404 - 'mxchat-embed'
4605 +
4606 + add_settings_field(
4607 + 'rate_limits',
4608 + __('Rate Limits Settings', 'mxchat'),
4609 + array($this, 'mxchat_rate_limits_callback'),
4610 + 'mxchat-chatbot',
4611 + 'mxchat_chatbot_section'
3405 4612 );
3406 4613
3407 4614 // Loops Settings Section
3408 4615 add_settings_section(
@@ -3411,43 +4618,10 @@
3411 4618 null,
3412 4619 'mxchat-embed'
3413 4620 );
3414 4621
3415 - // WooCommerce Settings Fields
4622 + // Loops Settings Fields (API Key moved to API Keys tab)
3416 4623 add_settings_field(
3417 - 'enable_woocommerce_integration',
3418 - esc_html__('Automatically Embed Products', 'mxchat'),
3419 - array($this, 'mxchat_enable_woocommerce_integration_callback'),
3420 - 'mxchat-embed',
3421 - 'mxchat_woocommerce_section'
3422 - );
3423 -
3424 - add_settings_field(
3425 - 'woocommerce_consumer_key',
3426 - esc_html__('WooCommerce Consumer Key', 'mxchat'),
3427 - array($this, 'mxchat_woocommerce_consumer_key_callback'),
3428 - 'mxchat-embed',
3429 - 'mxchat_woocommerce_section'
3430 - );
3431 -
3432 - add_settings_field(
3433 - 'woocommerce_consumer_secret',
3434 - esc_html__('WooCommerce Consumer Secret', 'mxchat'),
3435 - array($this, 'mxchat_woocommerce_consumer_secret_callback'),
3436 - 'mxchat-embed',
3437 - 'mxchat_woocommerce_section'
3438 - );
3439 -
3440 - // Loops Settings Fields
3441 - add_settings_field(
3442 - 'loops_api_key',
3443 - esc_html__('Loops API Key', 'mxchat'),
3444 - array($this, 'mxchat_loops_api_key_callback'),
3445 - 'mxchat-embed',
3446 - 'mxchat_loops_section'
3447 - );
3448 -
3449 - add_settings_field(
3450 4624 'loops_mailing_list',
3451 4625 esc_html__('Loops Mailing List', 'mxchat'),
3452 4626 array($this, 'mxchat_loops_mailing_list_callback'),
3453 4627 'mxchat-embed',
@@ -3477,17 +4651,10 @@
3477 4651 array($this, 'mxchat_brave_section_callback'),
3478 4652 'mxchat-embed'
3479 4653 );
3480 4654
4655 + // Brave API Key moved to API Keys tab
3481 4656 add_settings_field(
3482 - 'brave_api_key',
3483 - __('Brave API Key', 'mxchat'),
3484 - array($this, 'mxchat_brave_api_key_callback'),
3485 - 'mxchat-embed',
3486 - 'mxchat_brave_section'
3487 - );
3488 -
3489 - add_settings_field(
3490 4657 'brave_image_count',
3491 4658 __('Number of Images to Return', 'mxchat'),
3492 4659 array($this, 'mxchat_brave_image_count_callback'),
3493 4660 'mxchat-embed',
@@ -3541,9 +4708,27 @@
3541 4708 'mxchat-embed',
3542 4709 'mxchat_pdf_intent_section'
3543 4710 );
3544 4711
4712 + // PDF Upload Button Toggle
3545 4713 add_settings_field(
4714 + 'show_pdf_upload_button',
4715 + __('Show PDF Upload Button', 'mxchat'),
4716 + array($this, 'mxchat_show_pdf_upload_button_callback'),
4717 + 'mxchat-embed',
4718 + 'mxchat_pdf_intent_section'
4719 + );
4720 +
4721 + // Word Upload Button Toggle
4722 + add_settings_field(
4723 + 'show_word_upload_button',
4724 + __('Show Word Upload Button', 'mxchat'),
4725 + array($this, 'mxchat_show_word_upload_button_callback'),
4726 + 'mxchat-embed',
4727 + 'mxchat_pdf_intent_section'
4728 + );
4729 +
4730 + add_settings_field(
3546 4731 'pdf_intent_trigger_text',
3547 4732 __('Intent Trigger Text', 'mxchat'),
3548 4733 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3549 4734 'mxchat-embed',
@@ -3608,8 +4793,16 @@
3608 4793 'mxchat_live_agent_section'
3609 4794 );
3610 4795
3611 4796 add_settings_field(
4797 + 'live_agent_user_ids',
4798 + __('Slack Agent User IDs', 'mxchat'),
4799 + array($this, 'mxchat_live_agent_user_ids_callback'),
4800 + 'mxchat-embed',
4801 + 'mxchat_live_agent_section'
4802 + );
4803 +
4804 + add_settings_field(
3612 4805 'live_agent_webhook_url',
3613 4806 __('Slack Webhook URL', 'mxchat'),
3614 4807 array($this, 'mxchat_live_agent_webhook_url_callback'),
3615 4808 'mxchat-embed',
@@ -3638,9 +4831,9 @@
3638 4831
3639 4832 // General Settings Section
3640 4833 add_settings_section(
3641 4834 'mxchat_general_section',
3642 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
4835 + esc_html__('YouTube Tutorials', 'mxchat'),
3643 4836 null,
3644 4837 'mxchat-general'
3645 4838 );
3646 4839 }
@@ -3645,9 +4838,8 @@
3645 4838 );
3646 4839 }
3647 4840
3648 4841 public function mxchat_prompts_page_init() {
3649 - // Register all settings as a single array
3650 4842 register_setting(
3651 4843 'mxchat_prompts_options',
3652 4844 'mxchat_prompts_options',
3653 4845 array(
@@ -3659,18 +4851,57 @@
3659 4851 'mxchat_use_pinecone' => 0,
3660 4852 'mxchat_pinecone_api_key' => '',
3661 4853 'mxchat_pinecone_environment' => '',
3662 4854 'mxchat_pinecone_index' => '',
3663 - 'mxchat_pinecone_host' => '', // Add this line
4855 + 'mxchat_pinecone_host' => '',
3664 4856 ),
3665 4857 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3666 4858 )
3667 4859 );
3668 4860
3669 - // Add settings saved message
3670 4861 add_action('admin_notices', array($this, 'sync_settings_notice'));
3671 4862 }
3672 4863
4864 +public function mxchat_transcripts_page_init() {
4865 + register_setting(
4866 + 'mxchat_transcripts_options',
4867 + 'mxchat_transcripts_options',
4868 + array(
4869 + 'type' => 'array',
4870 + 'description' => __('MXChat Transcripts Notification Settings', 'mxchat'),
4871 + 'default' => array(
4872 + 'mxchat_enable_notifications' => 0,
4873 + 'mxchat_notification_email' => get_option('admin_email'),
4874 + ),
4875 + 'sanitize_callback' => array($this, 'sanitize_transcripts_options'),
4876 + )
4877 + );
4878 +
4879 + add_settings_section(
4880 + 'mxchat_transcripts_notification_section',
4881 + esc_html__('Chat Notification Settings', 'mxchat'),
4882 + array($this, 'mxchat_transcripts_notification_section_callback'),
4883 + 'mxchat-transcripts'
4884 + );
4885 +
4886 + add_settings_field(
4887 + 'mxchat_enable_notifications',
4888 + esc_html__('Enable Chat Notifications', 'mxchat'),
4889 + array($this, 'mxchat_enable_notifications_callback'),
4890 + 'mxchat-transcripts',
4891 + 'mxchat_transcripts_notification_section'
4892 + );
4893 +
4894 + add_settings_field(
4895 + 'mxchat_notification_email',
4896 + esc_html__('Notification Email Address', 'mxchat'),
4897 + array($this, 'mxchat_notification_email_callback'),
4898 + 'mxchat-transcripts',
4899 + 'mxchat_transcripts_notification_section'
4900 + );
4901 +}
4902 +
4903 +
3673 4904 /**
3674 4905 * Sanitize all prompts options
3675 4906 *
3676 4907 * @param array $input The unsanitized options array
@@ -3729,9 +4960,8 @@
3729 4960
3730 4961 return $sanitized;
3731 4962 }
3732 4963
3733 -
3734 4964 public function sync_settings_notice() {
3735 4965 // Only show notice on our plugin page
3736 4966 if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3737 4967 return;
@@ -3752,183 +4982,129 @@
3752 4982 public function sanitize_sync_setting($input) {
3753 4983 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3754 4984 }
3755 4985
4986 +public function mxchat_rate_limits_callback() {
4987 + $all_options = get_option('mxchat_options', []);
3756 4988
4989 + // Define available rate limits
4990 + $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
3757 4991
3758 -public function mxchat_handle_activate_license() {
3759 - // Check nonce
3760 - if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3761 - wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
3762 - return;
3763 - }
4992 + // Define available timeframes
4993 + $timeframes = array(
4994 + 'hourly' => __('Per Hour', 'mxchat'),
4995 + 'daily' => __('Per Day', 'mxchat'),
4996 + 'weekly' => __('Per Week', 'mxchat'),
4997 + 'monthly' => __('Per Month', 'mxchat')
4998 + );
3764 4999
3765 - // Verify user capabilities
3766 - if (!current_user_can('manage_options')) {
3767 - wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
3768 - return;
3769 - }
5000 + // Get all roles plus a "logged_out" pseudo-role
5001 + $roles = wp_roles()->get_names();
5002 + $roles['logged_out'] = __('Logged Out Users', 'mxchat');
3770 5003
3771 - $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3772 - $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
5004 + // Start the wrapper
5005 + echo '<div class="pro-feature-wrapper active">';
5006 + echo '<div class="mxchat-rate-limits-container">';
3773 5007
3774 - if (empty($license_key) || empty($customer_email)) {
3775 - wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
3776 - return;
3777 - }
5008 + echo '<p class="description" style="margin-bottom: 20px;">' .
5009 + esc_html__('Set message limits for each user role and customize the experience when users reach those limits. You can use {limit}, {timeframe}, {count}, and {remaining} as placeholders.', 'mxchat') .
5010 + '</p>';
3778 5011
3779 - $product_id = 'MxChatPRO';
3780 - $response = wp_remote_get(
3781 - add_query_arg(
3782 - array(
3783 - 'wc-api' => 'software-api',
3784 - 'request' => 'activation',
3785 - 'email' => $customer_email,
3786 - 'license_key' => $license_key,
3787 - 'product_id' => $product_id
3788 - ),
3789 - 'http://mxchat.ai/'
3790 - )
3791 - );
5012 + // Add markdown link documentation
5013 + echo '<div class="notice notice-info inline" style="margin-bottom: 20px; padding: 10px;">';
5014 + echo '<p><strong>' . esc_html__('Markdown Links Supported:', 'mxchat') . '</strong></p>';
5015 + echo '<p>' . esc_html__('You can include clickable links in your custom messages using markdown syntax:', 'mxchat') . '</p>';
5016 + echo '<ul style="margin-left: 20px;">';
5017 + echo '<li><code>[Link text](https://example.com)</code> - Creates a clickable link</li>';
5018 + echo '<li><code>[Visit our pricing](https://example.com/pricing)</code> - Link with custom text</li>';
5019 + echo '<li><code>Plain URLs like https://example.com will also become clickable</code></li>';
5020 + echo '</ul>';
5021 + echo '</div>';
3792 5022
3793 - if (is_wp_error($response)) {
3794 - wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message());
3795 - return;
3796 - }
5023 + // Output the controls for each role
5024 + foreach ($roles as $role_id => $role_name) {
5025 + // Get saved options or defaults
5026 + $default_limit = ($role_id === 'logged_out') ? '10' : '100';
5027 + $default_timeframe = 'daily';
5028 + $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
3797 5029
3798 - $body = wp_remote_retrieve_body($response);
3799 - $data = json_decode($body);
5030 + $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
5031 + ? $all_options['rate_limits'][$role_id]['limit']
5032 + : $default_limit;
3800 5033
3801 - if ($data && isset($data->activated) && $data->activated) {
3802 - update_option('mxchat_license_status', 'active');
3803 - update_option('mxchat_pro_email', $customer_email);
3804 - update_option('mxchat_activation_key', $license_key);
3805 - wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
3806 - } else {
3807 - $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
3808 - update_option('mxchat_license_status', 'inactive');
3809 - update_option('mxchat_license_error', $error_message);
3810 - wp_send_json_error($error_message);
3811 - }
3812 -}
5034 + $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
5035 + ? $all_options['rate_limits'][$role_id]['timeframe']
5036 + : $default_timeframe;
3813 5037
5038 + $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
5039 + ? $all_options['rate_limits'][$role_id]['message']
5040 + : $default_message;
3814 5041
3815 -public function mxchat_rate_limit_logged_out_callback() {
3816 - // Load the entire 'mxchat_options' array
3817 - $all_options = get_option('mxchat_options', []);
5042 + // Output the row
5043 + echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
3818 5044
3819 - // Retrieve the saved rate limit or use the default value
3820 - $default_rate_limit = '10';
3821 - $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
5045 + // Role label
5046 + echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
3822 5047
3823 - // Define available rate limits
3824 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5048 + // Controls section
5049 + echo '<div class="mxchat-rate-limit-controls-wrapper">';
3825 5050
3826 - // Output the dropdown
3827 - echo '<div class="pro-feature-wrapper active">';
3828 - echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3829 - foreach ($rate_limits as $limit) {
3830 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3831 - }
3832 - echo '</select>';
3833 - echo '<p class="description">' . esc_html__('Set the maximum number of messages a logged-out user can send within a 24-hour period.', 'mxchat') . '</p>';
3834 - echo '</div>';
3835 -}
5051 + // Rate limit and timeframe controls
5052 + echo '<div class="mxchat-rate-limit-controls">';
3836 5053
3837 -// Add this new callback function
3838 -public function mxchat_rate_limit_roles_callback() {
3839 - $all_options = get_option('mxchat_options', []);
3840 - $roles = wp_roles()->get_names();
3841 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3842 -
3843 - echo '<div class="pro-feature-wrapper active mxchat-autosave-section">';
3844 -
3845 - foreach ($roles as $role_id => $role_name) {
3846 - $default_rate_limit = '100';
3847 - $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id])
3848 - ? $all_options['role_rate_limits'][$role_id]
3849 - : $default_rate_limit;
3850 -
3851 - echo '<div style="margin-bottom: 10px;">';
3852 - echo '<label style="display: inline-block; width: 150px;">' . esc_html($role_name) . ':</label>';
5054 + // Limit dropdown
5055 + echo '<div>';
5056 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
3853 5057 echo '<select
3854 - id="role_rate_limits_' . esc_attr($role_id) . '"
3855 - name="mxchat_options[role_rate_limits][' . esc_attr($role_id) . ']"
5058 + id="rate_limits_' . esc_attr($role_id) . '_limit"
5059 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
3856 5060 class="mxchat-autosave-field">';
3857 5061 foreach ($rate_limits as $limit) {
3858 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
5062 + echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3859 5063 }
3860 5064 echo '</select>';
3861 5065 echo '</div>';
3862 - }
3863 5066
3864 - echo '<p class="description">' . esc_html__('Set the maximum number of messages users in each role can send within a 24-hour period.', 'mxchat') . '</p>';
3865 - echo '</div>';
3866 -}
5067 + // Timeframe dropdown
5068 + echo '<div>';
5069 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
5070 + echo '<select
5071 + id="rate_limits_' . esc_attr($role_id) . '_timeframe"
5072 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
5073 + class="mxchat-autosave-field">';
5074 + foreach ($timeframes as $value => $label) {
5075 + echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
5076 + }
5077 + echo '</select>';
5078 + echo '</div>';
3867 5079
3868 -public function mxchat_rate_limit_message_callback() {
3869 - // Load the entire 'mxchat_options' array
3870 - $all_options = get_option('mxchat_options', []);
5080 + echo '</div>'; // End controls
3871 5081
3872 - // Retrieve the saved message or use the default value
3873 - $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
3874 - $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
5082 + // Custom message textarea
5083 + echo '<div class="mxchat-rate-limit-message">';
5084 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
5085 + echo '<textarea
5086 + id="rate_limits_' . esc_attr($role_id) . '_message"
5087 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
5088 + class="mxchat-autosave-field"
5089 + placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
5090 + esc_textarea($custom_message) .
5091 + '</textarea>';
5092 + echo '<p class="description">' .
5093 + esc_html__('Example: Rate limit reached! [Visit our pricing page](https://example.com/pricing) to upgrade.', 'mxchat') .
5094 + '</p>';
5095 + echo '</div>'; // End message
3875 5096
3876 - // Output the textarea
3877 - echo '<div class="pro-feature-wrapper active">';
3878 - printf(
3879 - '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
3880 - esc_textarea($rate_limit_message)
3881 - );
3882 - echo '<p class="description">' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '</p>';
3883 - echo '</div>';
3884 -}
5097 + echo '</div>'; // End controls wrapper
3885 5098
5099 + echo '</div>'; // End row
5100 + }
3886 5101
3887 -public function mxchat_enable_woocommerce_integration_callback() {
3888 - // Load from mxchat_options array
3889 - $options = get_option('mxchat_options', []);
5102 + echo '</div>'; // End container
3890 5103
3891 - // Get WooCommerce integration status with backwards compatibility
3892 - $woo_integration = isset($options['enable_woocommerce_integration'])
3893 - ? $options['enable_woocommerce_integration']
3894 - : '';
3895 -
3896 - // Support both old ('1') and new ('on') values
3897 - $checked = ($woo_integration === 'on' || $woo_integration === '1') ? 'checked' : '';
3898 -
3899 - // Check if the feature is activated (paid feature)
3900 - $disabled = $this->is_activated ? '' : 'disabled';
3901 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3902 -
3903 - echo '<div class="' . esc_attr($class) . '">';
3904 -
3905 - echo '<label class="toggle-switch">';
3906 - echo sprintf(
3907 - '<input type="checkbox" id="enable_woocommerce_integration" name="enable_woocommerce_integration" value="on" %s %s />',
3908 - esc_attr($checked),
3909 - esc_attr($disabled)
3910 - );
3911 - echo '<span class="slider"></span>';
3912 - echo '</label>';
3913 -
3914 - echo '<p class="description">' . esc_html__('Automatically syncs new product additions, updates, and removals to the knowledge base. For existing products, submit your product sitemap in the Knowledge Base section to add them initially.', 'mxchat') . '</p>';
3915 -
3916 - // Pro feature overlay for non-activated users
3917 - if (!$this->is_activated) {
3918 - echo '<div class="pro-feature-overlay">';
3919 - echo '<a href="https://mxchat.ai/" target="_blank">';
3920 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
3921 - echo '</a>';
3922 - echo '</div>';
3923 - }
3924 -
3925 - echo '</div>';
5104 + echo '</div>'; // End pro-feature-wrapper
3926 5105 }
3927 5106
3928 -
3929 -
3930 -
3931 5107 private function mxchat_add_option_field($id, $title, $callback = '') {
3932 5108 add_settings_field(
3933 5109 $id,
3934 5110 __($title, 'mxchat'),
@@ -3938,162 +5114,105 @@
3938 5114 $id === 'model' ? ['label_for' => 'model'] : []
3939 5115 );
3940 5116 }
3941 5117
5118 +// API Keys Section Callback
5119 +public function mxchat_api_keys_section_callback() {
5120 + echo '<p>' . esc_html__('Manage all your API keys in one place. Add the API keys for the services you want to use with your chatbot.', 'mxchat') . '</p>';
5121 +}
5122 +
5123 +// OpenAI API Key
3942 5124 public function api_key_callback() {
3943 - // Retrieve from your stored 'api_key' in the mxchat_options array
3944 5125 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3945 5126
3946 - // Notice we changed the name to "api_key" (no array notation).
3947 - echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
5127 + echo '<div class="api-key-wrapper">';
5128 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
3948 5129 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3949 - echo '<p class="description">' . esc_html__('The OpenAI API key is required even when using other models as it is used for vector embedding functionality. You must add credits to your OpenAI billing account before use.', 'mxchat') . '</p>';
5130 + echo '<p class="description">' . esc_html__('Required for OpenAI GPT models and OpenAI embeddings. Get your API key from OpenAI Platform.', 'mxchat') . '</p>';
5131 + echo '</div>';
3950 5132 }
5133 +
5134 +// X.AI API Key
3951 5135 public function xai_api_key_callback() {
3952 - // Check if the feature is activated (paid feature)
3953 - $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3954 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3955 -
3956 - // Retrieve the X.AI API key value
3957 5136 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3958 5137
3959 - // Render the input field for the X.AI API key
3960 - echo '<div class="' . esc_attr($class) . '">';
3961 - printf(
3962 - '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
3963 - $xaiApiKey,
3964 - $disabled
3965 - );
5138 + echo '<div class="api-key-wrapper">';
5139 + echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
3966 5140 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3967 -
3968 - // If the feature is not activated, show the overlay with a "Pro Only" message
3969 - if (!$this->is_activated) {
3970 - echo '<div class="pro-feature-overlay">';
3971 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
3972 - echo '</div>';
3973 - }
3974 -
5141 + echo '<p class="description">' . esc_html__('Required for X.AI Grok models. Get your API key from X.AI Console.', 'mxchat') . '</p>';
3975 5142 echo '</div>';
3976 5143 }
5144 +// Claude API Key
3977 5145 public function claude_api_key_callback() {
3978 - // Check if the feature is activated (paid feature)
3979 - $disabled = $this->is_activated ? '' : 'disabled';
3980 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3981 -
3982 - // Retrieve the Claude API key value directly like the others
3983 5146 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3984 -
3985 - // Use direct name field like the other working API keys
3986 - echo '<div class="' . esc_attr($class) . '">';
3987 - printf(
3988 - '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
3989 - $claudeApiKey,
3990 - $disabled
3991 - );
5147 +
5148 + echo '<div class="api-key-wrapper">';
5149 + echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
3992 5150 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3993 -
3994 - if (!$this->is_activated) {
3995 - echo '<div class="pro-feature-overlay">';
3996 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
3997 - echo '</div>';
3998 - }
5151 + echo '<p class="description">' . esc_html__('Required for Anthropic Claude models. Get your API key from Anthropic Console.', 'mxchat') . '</p>';
3999 5152 echo '</div>';
4000 5153 }
5154 +
5155 +// DeepSeek API Key
4001 5156 public function deepseek_api_key_callback() {
4002 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
4003 5157 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4004 5158
4005 - // Notice we changed the name to "deepseek_api_key" (no array notation).
4006 - echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" />';
5159 + echo '<div class="api-key-wrapper">';
5160 + echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4007 5161 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5162 + echo '<p class="description">' . esc_html__('Required for DeepSeek models. Get your API key from DeepSeek Platform.', 'mxchat') . '</p>';
5163 + echo '</div>';
4008 5164 }
4009 5165
5166 +// Gemini API Key
5167 +public function gemini_api_key_callback() {
5168 + $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
4010 5169
4011 -public function mxchat_woocommerce_consumer_key_callback() {
4012 - // Load the entire 'mxchat_options' array
4013 - $all_options = get_option('mxchat_options', []);
5170 + echo '<div class="api-key-wrapper">';
5171 + echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
5172 + echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5173 + echo '<p class="description">' . esc_html__('Required for Google Gemini models and embeddings. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
5174 + echo '</div>';
5175 +}
4014 5176
4015 - // Retrieve the WooCommerce consumer key or set a default
4016 - $consumer_key = isset($all_options['woocommerce_consumer_key']) ? $all_options['woocommerce_consumer_key'] : '';
4017 5177
4018 - // Check if the feature is activated (paid feature)
4019 - $disabled = $this->is_activated ? '' : 'disabled';
4020 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4021 -
4022 - echo '<div class="' . esc_attr($class) . '">';
4023 -
4024 - // Render the input field
4025 - printf(
4026 - '<input type="text" id="woocommerce_consumer_key" name="woocommerce_consumer_key" value="%s" class="regular-text" %s />',
4027 - esc_attr($consumer_key),
4028 - esc_attr($disabled)
4029 - );
4030 -
4031 - // Description for the input field
4032 - echo '<p class="description">' . esc_html__('Enter your WooCommerce consumer key for integration.', 'mxchat') . '</p>';
4033 -
4034 - // Pro feature overlay for non-activated users
4035 - if (!$this->is_activated) {
4036 - echo '<div class="pro-feature-overlay">';
4037 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
4038 - echo '</div>';
4039 - }
4040 -
5178 +// OpenRouter API Key
5179 +public function openrouter_api_key_callback() {
5180 + $openrouterApiKey = isset($this->options['openrouter_api_key']) ? esc_attr($this->options['openrouter_api_key']) : '';
5181 + echo '<div class="api-key-wrapper">';
5182 + echo '<input type="password" id="openrouter_api_key" name="openrouter_api_key" value="' . $openrouterApiKey . '" class="regular-text" autocomplete="off" />';
5183 + echo '<button type="button" id="toggleOpenRouterApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5184 + echo '<p class="description">' . esc_html__('Required for OpenRouter models. Get your API key from OpenRouter.ai', 'mxchat') . '</p>';
4041 5185 echo '</div>';
4042 5186 }
4043 5187
4044 -public function mxchat_woocommerce_consumer_secret_callback() {
4045 - // Get value with fallback
4046 - $consumer_secret = isset($this->options['woocommerce_consumer_secret'])
4047 - ? esc_attr($this->options['woocommerce_consumer_secret'])
4048 - : '';
5188 +// Voyage API Key
5189 +public function voyage_api_key_callback() {
5190 + $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4049 5191
4050 - // Check if the feature is activated (paid feature)
4051 - $disabled = $this->is_activated ? '' : 'disabled';
4052 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4053 -
4054 - echo '<div class="' . esc_attr($class) . '">';
4055 -
4056 - // Output the password input
4057 - echo sprintf(
4058 - '<input type="password"
4059 - id="woocommerce_consumer_secret"
4060 - name="woocommerce_consumer_secret"
4061 - value="%s"
4062 - class="regular-text"
4063 - %s />',
4064 - $consumer_secret,
4065 - esc_attr($disabled)
4066 - );
4067 -
4068 - // Add show/hide button
4069 - echo '<button type="button" id="toggleWooCommerceSecretVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4070 -
4071 - // Pro feature overlay
4072 - if (!$this->is_activated) {
4073 - echo '<div class="pro-feature-overlay">';
4074 - echo '<a href="https://mxchat.ai/" target="_blank">';
4075 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4076 - echo '</a>';
4077 - echo '</div>';
4078 - }
4079 -
5192 + echo '<div class="api-key-wrapper">';
5193 + echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5194 + echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5195 + echo '<p class="description">' . esc_html__('Required for Voyage AI embedding models. Get your API key from Voyage AI.', 'mxchat') . '</p>';
4080 5196 echo '</div>';
4081 5197 }
5198 +
4082 5199 public function mxchat_loops_api_key_callback() {
4083 - // Support both old and new format
4084 5200 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4085 5201
5202 + // Hidden fields to "trap" autofill
5203 + echo '<input type="text" style="display:none" autocomplete="username" />';
5204 + echo '<input type="password" style="display:none" autocomplete="current-password" />';
5205 +
4086 5206 echo '<div class="api-key-wrapper">';
4087 5207 echo sprintf(
4088 - '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
5208 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
4089 5209 $loops_api_key
4090 5210 );
4091 5211 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4092 5212 echo '</div>';
4093 - echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5213 + echo '<p class="description">' . esc_html__('Required for Loops email integration. Get your API key from Loops.so', 'mxchat') . '</p>';
4094 5214 }
4095 -
4096 5215 public function mxchat_loops_mailing_list_callback() {
4097 5216 // Add error handling and type checking
4098 5217 $loops_api_key = '';
4099 5218 $selected_list = '';
@@ -4111,8 +5230,12 @@
4111 5230 if (!empty($loops_api_key)) {
4112 5231 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4113 5232 if (is_array($lists) && !empty($lists)) {
4114 5233 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
5234 +
5235 + // Add a default "Select a list" option
5236 + echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
5237 +
4115 5238 foreach ($lists as $list) {
4116 5239 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4117 5240 echo sprintf(
4118 5241 '<option value="%s" %s>%s</option>',
@@ -4122,8 +5245,9 @@
4122 5245 );
4123 5246 }
4124 5247 }
4125 5248 echo '</select>';
5249 + echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
4126 5250 } else {
4127 5251 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4128 5252 }
4129 5253 } else {
@@ -4134,14 +5258,13 @@
4134 5258 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4135 5259 $triggered_response = isset($this->options['triggered_phrase_response'])
4136 5260 ? $this->options['triggered_phrase_response']
4137 5261 : $default_response;
4138 -
4139 5262 echo sprintf(
4140 5263 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4141 5264 esc_textarea($triggered_response)
4142 5265 );
4143 - echo '<p class="description">' . esc_html__('Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.', 'mxchat') . '</p>';
5266 + echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a trigger keyword is detected. The AI will use this as guidance to naturally ask for the user\'s email in a conversational way.', 'mxchat') . '</p>';
4144 5267 }
4145 5268
4146 5269 public function mxchat_email_capture_response_callback() {
4147 5270 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
@@ -4147,16 +5270,14 @@
4147 5270 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4148 5271 $email_capture_response = isset($this->options['email_capture_response'])
4149 5272 ? $this->options['email_capture_response']
4150 5273 : $default_response;
4151 -
4152 5274 echo sprintf(
4153 5275 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4154 5276 esc_textarea($email_capture_response)
4155 5277 );
4156 - echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
5278 + echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a user provides their email. The AI will use this as guidance to naturally confirm the email capture in a conversational way.', 'mxchat') . '</p>';
4157 5279 }
4158 -
4159 5280 public function mxchat_pre_chat_message_callback() {
4160 5281 // Load the entire 'mxchat_options' array
4161 5282 $all_options = get_option('mxchat_options', []);
4162 5283
@@ -4171,49 +5292,151 @@
4171 5292 );
4172 5293 echo '<p class="description">' . esc_html__('Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.', 'mxchat') . '</p>';
4173 5294 }
4174 5295
4175 -
4176 -
4177 5296 // Callback for AI Instructions textarea
4178 5297 public function system_prompt_instructions_callback() {
4179 5298 // Retrieve the current value of the system prompt instructions
4180 5299 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4181 -
4182 5300 // Render the textarea field
4183 5301 printf(
4184 5302 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4185 5303 $instructions
4186 5304 );
5305 + // Provide a helpful description with sample instructions button
5306 + echo '<p class="description">' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '</p>';
5307 + echo '<div class="mxchat-instructions-container">';
5308 + echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
5309 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5310 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5311 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5312 + echo '</svg>';
5313 + echo esc_html__('View Sample Instructions', 'mxchat');
5314 + echo '</button>';
5315 + echo '</div>';
4187 5316
4188 - // Provide a helpful description
4189 - echo '<p class="description">' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '</p>';
5317 + // Add modal to WordPress admin footer instead of inline
5318 + add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
4190 5319 }
4191 5320
5321 +// New method to render modal in admin footer
5322 +public function render_sample_instructions_modal() {
5323 + static $modal_rendered = false;
5324 + if ($modal_rendered) return; // Prevent duplicate modals
5325 + $modal_rendered = true;
4192 5326
5327 + echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
5328 + echo '<div class="mxchat-instructions-modal-content">';
5329 + echo '<div class="mxchat-instructions-modal-header">';
5330 + echo '<h3 class="mxchat-instructions-modal-title">';
5331 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5332 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5333 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5334 + echo '</svg>';
5335 + echo esc_html__('Sample AI Instructions', 'mxchat');
5336 + echo '</h3>';
5337 + echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
5338 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5339 + echo '<line x1="18" y1="6" x2="6" y2="18"/>';
5340 + echo '<line x1="6" y1="6" x2="18" y2="18"/>';
5341 + echo '</svg>';
5342 + echo '</button>';
5343 + echo '</div>';
5344 + echo '<div class="mxchat-instructions-modal-body">';
5345 + echo '<div class="mxchat-instructions-content">';
5346 + echo esc_html('You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
5347 +
5348 +# Response Style - CRITICALLY IMPORTANT
5349 +- MAXIMUM LENGTH: 1-3 short sentences per response
5350 +- Ultra-concise: Get straight to the answer with no filler
5351 +- No introductions like "Sure!" or "I\'d be happy to help"
5352 +- No phrases like "based on my knowledge" or "according to information"
5353 +- No explanatory text before giving the answer
5354 +- No summaries or repetition
5355 +- Hyperlink all URLs
5356 +- Respond in user\'s language
5357 +- Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
5358 +
5359 +# Knowledge Base Requirements - PREVENT HALLUCINATIONS
5360 +- ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
5361 +- If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
5362 +- NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
5363 +- When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
5364 +- Better to admit insufficient information than provide inaccurate answers');
5365 + echo '</div>';
5366 + echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
5367 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5368 + echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
5369 + echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
5370 + echo '</svg>';
5371 + echo esc_html__('Copy Instructions', 'mxchat');
5372 + echo '</button>';
5373 + echo '</div>';
5374 + echo '<div class="mxchat-instructions-modal-footer">';
5375 + echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
5376 + echo '</div>';
5377 + echo '</div>';
5378 + echo '</div>';
5379 +}
5380 +
5381 +
4193 5382 public function mxchat_model_callback() {
4194 5383 // Define available models grouped by provider
4195 5384 $models = array(
5385 + esc_html__('OpenRouter (100+ Models)', 'mxchat') => array(
5386 + 'openrouter' => esc_html__('OpenRouter - Select after entering API key', 'mxchat'),
5387 + ),
5388 + esc_html__('Google Gemini Models', 'mxchat') => array(
5389 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5390 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5391 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5392 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5393 + ),
5394 + esc_html__('Google Gemini Models', 'mxchat') => array(
5395 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5396 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5397 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5398 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5399 + ),
4196 5400 esc_html__('X.AI Models', 'mxchat') => array(
4197 - 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4198 - 'grok-2' => esc_html__('Grok 2', 'mxchat')
5401 + 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
5402 + 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
5403 + 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
5404 + 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
5405 + 'grok-2' => esc_html__('Grok 2', 'mxchat'),
5406 + 'grok-4-0709' => esc_html__('Grok 4 (Latest Flagship)', 'mxchat'),
5407 + 'grok-4-1-fast-reasoning' => esc_html__('Grok 4.1 Fast (Reasoning)', 'mxchat'),
5408 + 'grok-4-1-fast-non-reasoning' => esc_html__('Grok 4.1 Fast (Non-Reasoning)', 'mxchat'),
4199 5409 ),
4200 5410 esc_html__('DeepSeek Models', 'mxchat') => array(
4201 5411 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4202 5412 ),
4203 5413 esc_html__('Claude Models', 'mxchat') => array(
4204 - 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
4205 - 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
5414 + 'claude-sonnet-4-5-20250929' => esc_html__('Claude Sonnet 4.5 (Best for Agents & Coding)', 'mxchat'),
5415 + 'claude-opus-4-1-20250805' => esc_html__('Claude Opus 4.1 (Exceptional for Complex Tasks)', 'mxchat'),
5416 + 'claude-haiku-4-5-20251001' => esc_html__('Claude Haiku 4.5 (Fastest & Most Intelligent)', 'mxchat'),
5417 + 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
5418 + 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
5419 + 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
5420 + 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
4206 5421 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4207 - 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
5422 + 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat'),
4208 5423 ),
4209 5424 esc_html__('OpenAI Models', 'mxchat') => array(
5425 + // GPT-5 family
5426 + 'gpt-5.1-2025-11-13' => esc_html__('GPT-5.1 (Flagship for Coding & Agentic Tasks)', 'mxchat'),
5427 + 'gpt-5' => esc_html__('GPT-5 (Flagship for Coding, Reasoning & Agents)', 'mxchat'),
5428 + 'gpt-5-mini' => esc_html__('GPT-5 Mini (Faster, Cost-Efficient for Precise Prompts)', 'mxchat'),
5429 + 'gpt-5-nano' => esc_html__('GPT-5 Nano (Fastest & Cheapest for Summarization/Classification)', 'mxchat'),
5430 +
5431 + // Existing OpenAI models
5432 + 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4210 5433 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4211 5434 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4212 5435 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4213 5436 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4214 - 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4215 - )
5437 + 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat'),
5438 + ),
4216 5439 );
4217 5440
4218 5441 // Retrieve the currently selected model from saved options
4219 5442 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
@@ -4218,9 +5441,9 @@
4218 5441 // Retrieve the currently selected model from saved options
4219 5442 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4220 5443
4221 5444 // Begin the select dropdown
4222 - echo '<select id="model" name="model">'; // No array notation in name attribute
5445 + echo '<select id="model" name="model">';
4223 5446
4224 5447 // Iterate over groups of models
4225 5448 foreach ($models as $group_label => $group_models) {
4226 5449 echo '<optgroup label="' . esc_attr($group_label) . '">';
@@ -4225,28 +5448,228 @@
4225 5448 foreach ($models as $group_label => $group_models) {
4226 5449 echo '<optgroup label="' . esc_attr($group_label) . '">';
4227 5450
4228 5451 foreach ($group_models as $model_value => $model_label) {
4229 - // Disable Pro-only models for non-activated users
4230 - $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4231 - $label_suffix = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? esc_html__(' (Pro Only)', 'mxchat') : '';
4232 -
4233 - // Output the option element
4234 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
5452 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4235 5453 }
4236 5454
4237 5455 echo '</optgroup>';
4238 5456 }
4239 5457
4240 - // Close the select dropdown
4241 5458 echo '</select>';
4242 5459
4243 - // Add a description below the dropdown
4244 - echo '<p class="description">' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
5460 + echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
5461 +
5462 + // Add a note for OpenRouter
5463 + echo '<p class="description" id="openrouter-model-note" style="display:none; color: #d63638; font-weight: 500;">';
5464 + echo '<span class="dashicons dashicons-info" style="font-size: 16px; vertical-align: middle;"></span> ';
5465 + echo esc_html__('After entering your OpenRouter API key above, click the button below to load available models.', 'mxchat');
5466 + echo '</p>';
5467 +
5468 + // API Key Status Messages (hidden by default, shown by JS based on selected model)
5469 + $has_openai_key = !empty($this->options['api_key']);
5470 + $has_claude_key = !empty($this->options['claude_api_key']);
5471 + $has_xai_key = !empty($this->options['xai_api_key']);
5472 + $has_deepseek_key = !empty($this->options['deepseek_api_key']);
5473 + $has_gemini_key = !empty($this->options['gemini_api_key']);
5474 + $has_openrouter_key = !empty($this->options['openrouter_api_key']);
5475 +
5476 + // OpenAI/GPT models
5477 + echo '<p class="mxchat-api-status" data-provider="openai" style="display:none;">';
5478 + if ($has_openai_key) {
5479 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
5480 + } else {
5481 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5482 + }
5483 + echo '</p>';
5484 +
5485 + // Claude models
5486 + echo '<p class="mxchat-api-status" data-provider="claude" style="display:none;">';
5487 + if ($has_claude_key) {
5488 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Anthropic (Claude) detected', 'mxchat') . '</span>';
5489 + } else {
5490 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Anthropic (Claude) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5491 + }
5492 + echo '</p>';
5493 +
5494 + // X.AI models
5495 + echo '<p class="mxchat-api-status" data-provider="xai" style="display:none;">';
5496 + if ($has_xai_key) {
5497 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for X.AI (Grok) detected', 'mxchat') . '</span>';
5498 + } else {
5499 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for X.AI (Grok) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5500 + }
5501 + echo '</p>';
5502 +
5503 + // DeepSeek models
5504 + echo '<p class="mxchat-api-status" data-provider="deepseek" style="display:none;">';
5505 + if ($has_deepseek_key) {
5506 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for DeepSeek detected', 'mxchat') . '</span>';
5507 + } else {
5508 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for DeepSeek detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5509 + }
5510 + echo '</p>';
5511 +
5512 + // Gemini models
5513 + echo '<p class="mxchat-api-status" data-provider="gemini" style="display:none;">';
5514 + if ($has_gemini_key) {
5515 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
5516 + } else {
5517 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5518 + }
5519 + echo '</p>';
5520 +
5521 + // OpenRouter models
5522 + echo '<p class="mxchat-api-status" data-provider="openrouter" style="display:none;">';
5523 + if ($has_openrouter_key) {
5524 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenRouter detected', 'mxchat') . '</span>';
5525 + } else {
5526 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenRouter detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5527 + }
5528 + echo '</p>';
5529 +
5530 + // ADD THESE HIDDEN FIELDS RIGHT HERE:
5531 + $openrouter_model = isset($this->options['openrouter_selected_model']) ? esc_attr($this->options['openrouter_selected_model']) : '';
5532 + $openrouter_model_name = isset($this->options['openrouter_selected_model_name']) ? esc_attr($this->options['openrouter_selected_model_name']) : '';
5533 +
5534 + echo '<input type="hidden" id="openrouter_selected_model" name="openrouter_selected_model" value="' . $openrouter_model . '" />';
5535 + echo '<input type="hidden" id="openrouter_selected_model_name" name="openrouter_selected_model_name" value="' . $openrouter_model_name . '" />';
4245 5536 }
4246 5537
5538 +// Update your existing callback method
5539 +public function enable_streaming_toggle_callback() {
5540 + // Get value from options array, default to 'on'
5541 + $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
5542 + $checked = ($enabled === 'on') ? 'checked' : '';
4247 5543
5544 + echo '<label class="toggle-switch">';
5545 + echo sprintf(
5546 + '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
5547 + esc_attr($checked)
5548 + );
5549 + echo '<span class="slider"></span>';
5550 + echo '</label>';
5551 + echo '<p class="description">' .
5552 + esc_html__('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, and Grok). When disabled, responses will load all at once.', 'mxchat') .
5553 + '</p>';
4248 5554
5555 + // Test button with better styling
5556 + echo '<div style="margin-top: 15px;">';
5557 + echo '<button type="button" id="mxchat-test-streaming-btn" class="button button-secondary">';
5558 + echo '<span class="dashicons dashicons-admin-tools" style="vertical-align: middle; margin-right: 5px;"></span>';
5559 + echo esc_html__('Test Streaming Compatibility', 'mxchat');
5560 + echo '</button>';
5561 + echo '<p id="mxchat-test-streaming-result" style="margin-top:10px; font-weight: bold;"></p>';
5562 + echo '</div>';
5563 +}
5564 +
5565 +// AJAX handler to fetch OpenRouter models
5566 +public function fetch_openrouter_models() {
5567 + check_ajax_referer('mxchat_fetch_openrouter_models', 'nonce');
5568 +
5569 + $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
5570 +
5571 + if (empty($api_key)) {
5572 + wp_send_json_error(array('message' => 'API key is required'));
5573 + }
5574 +
5575 + $response = wp_remote_get('https://openrouter.ai/api/v1/models', array(
5576 + 'headers' => array(
5577 + 'Authorization' => 'Bearer ' . $api_key,
5578 + 'Content-Type' => 'application/json',
5579 + ),
5580 + 'timeout' => 15,
5581 + ));
5582 +
5583 + if (is_wp_error($response)) {
5584 + wp_send_json_error(array('message' => $response->get_error_message()));
5585 + }
5586 +
5587 + $body = wp_remote_retrieve_body($response);
5588 + $data = json_decode($body, true);
5589 +
5590 + if (isset($data['data']) && is_array($data['data'])) {
5591 + // Format the models for the frontend
5592 + $models = array_map(function($model) {
5593 + return array(
5594 + 'id' => $model['id'],
5595 + 'name' => $model['name'] ?? $model['id'],
5596 + 'description' => $model['description'] ?? '',
5597 + 'context_length' => $model['context_length'] ?? 0,
5598 + 'pricing' => array(
5599 + 'prompt' => $model['pricing']['prompt'] ?? 0,
5600 + 'completion' => $model['pricing']['completion'] ?? 0,
5601 + ),
5602 + );
5603 + }, $data['data']);
5604 +
5605 + wp_send_json_success(array('models' => $models));
5606 + } else {
5607 + wp_send_json_error(array('message' => 'Invalid response from OpenRouter'));
5608 + }
5609 +}
5610 +
5611 +// Callback function for embedding model selection
5612 +public function embedding_model_callback() {
5613 + $models = array(
5614 + esc_html__('OpenAI Embeddings', 'mxchat') => array(
5615 + 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
5616 + 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
5617 + 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
5618 + ),
5619 + esc_html__('Voyage AI Embeddings', 'mxchat') => array(
5620 + 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
5621 + ),
5622 + esc_html__('Google Gemini Embeddings', 'mxchat') => array(
5623 + 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
5624 + )
5625 + );
5626 + $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
5627 + echo '<select id="embedding_model" name="embedding_model">';
5628 + foreach ($models as $group_label => $group_models) {
5629 + echo '<optgroup label="' . esc_attr($group_label) . '">';
5630 + foreach ($group_models as $model_value => $model_label) {
5631 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
5632 + }
5633 + echo '</optgroup>';
5634 + }
5635 + echo '</select>';
5636 + echo '<p class="description"><span class="red-warning">IMPORTANT:</span> A vector embedding model is required for MxChat to function. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
5637 +
5638 + // API Key Status Messages for Embedding Models
5639 + $has_openai_key = !empty($this->options['api_key']);
5640 + $has_voyage_key = !empty($this->options['voyage_api_key']);
5641 + $has_gemini_key = !empty($this->options['gemini_api_key']);
5642 +
5643 + // OpenAI Embeddings
5644 + echo '<p class="mxchat-embedding-api-status" data-provider="openai" style="display:none;">';
5645 + if ($has_openai_key) {
5646 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
5647 + } else {
5648 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5649 + }
5650 + echo '</p>';
5651 +
5652 + // Voyage AI Embeddings
5653 + echo '<p class="mxchat-embedding-api-status" data-provider="voyage" style="display:none;">';
5654 + if ($has_voyage_key) {
5655 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Voyage AI detected', 'mxchat') . '</span>';
5656 + } else {
5657 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Voyage AI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5658 + }
5659 + echo '</p>';
5660 +
5661 + // Gemini Embeddings
5662 + echo '<p class="mxchat-embedding-api-status" data-provider="gemini" style="display:none;">';
5663 + if ($has_gemini_key) {
5664 + echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
5665 + } else {
5666 + echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5667 + }
5668 + echo '</p>';
5669 +}
5670 +
5671 +
4249 5672 public function mxchat_top_bar_title_callback() {
4250 5673 // Retrieve the current value of the top bar title from saved options
4251 5674 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4252 5675
@@ -4255,9 +5678,18 @@
4255 5678
4256 5679 // Add a description
4257 5680 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4258 5681 }
5682 +public function mxchat_ai_agent_text_callback() {
5683 + // Retrieve the current value of the AI agent text from saved options
5684 + $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
5685 + // Render the input field
5686 + echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
5687 + // Add a description
5688 + echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
5689 +}
4259 5690
5691 +
4260 5692 public function enable_email_block_callback() {
4261 5693 // Load full plugin options array
4262 5694 $all_options = get_option('mxchat_options', []);
4263 5695
@@ -4276,10 +5708,8 @@
4276 5708 echo '</label>';
4277 5709 echo '<p class="description">' . esc_html__('Their email will appear at the top of the transcript. Email form will show for users who are not logged in or have not provided an email within 24h.', 'mxchat') . '</p>';
4278 5710 }
4279 5711
4280 -
4281 -
4282 5712 public function email_blocker_header_content_callback() {
4283 5713 // Load the entire 'mxchat_options' array
4284 5714 $all_options = get_option('mxchat_options', []);
4285 5715
@@ -4287,14 +5717,15 @@
4287 5717 $content = isset($all_options['email_blocker_header_content'])
4288 5718 ? $all_options['email_blocker_header_content']
4289 5719 : '';
4290 5720
4291 - // Render the textarea
5721 + // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4292 5722 echo '<textarea
4293 5723 id="email_blocker_header_content"
4294 5724 name="email_blocker_header_content"
4295 5725 rows="5"
4296 5726 cols="70"
5727 + data-setting="email_blocker_header_content"
4297 5728 >' . esc_textarea($content) . '</textarea>';
4298 5729
4299 5730 echo '<p class="description">';
4300 5731 echo esc_html__('You may enter HTML here, such as &lt;h2&gt;Welcome&lt;/h2&gt; or &lt;p&gt;Let\'s get started&lt;/p&gt;.', 'mxchat');
@@ -4300,9 +5731,8 @@
4300 5731 echo esc_html__('You may enter HTML here, such as &lt;h2&gt;Welcome&lt;/h2&gt; or &lt;p&gt;Let\'s get started&lt;/p&gt;.', 'mxchat');
4301 5732 echo '</p>';
4302 5733 }
4303 5734
4304 -
4305 5735 public function email_blocker_button_text_callback() {
4306 5736 // Load the entire 'mxchat_options' array
4307 5737 $all_options = get_option('mxchat_options', []);
4308 5738
@@ -4318,28 +5748,55 @@
4318 5748 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4319 5749 echo '</p>';
4320 5750 }
4321 5751
5752 +//Enable name field callback
5753 +public function enable_name_field_callback() {
5754 + // Load full plugin options array
5755 + $all_options = get_option('mxchat_options', []);
5756 + // Get the value, default to 'off'
5757 + $enable_name_field = isset($all_options['enable_name_field']) ? $all_options['enable_name_field'] : 'off';
5758 + // Check if it's 'on'
5759 + $checked = ($enable_name_field === 'on') ? 'checked' : '';
5760 + echo '<label class="toggle-switch">';
5761 + echo sprintf(
5762 + '<input type="checkbox" id="enable_name_field" name="enable_name_field" value="on" %s />',
5763 + esc_attr($checked)
5764 + );
5765 + echo '<span class="slider"></span>';
5766 + echo '</label>';
5767 + echo '<p class="description">' . esc_html__('Enable this to also require users to enter their name along with their email before chatting.', 'mxchat') . '</p>';
5768 +}
5769 +//Name field placeholder callback
5770 +public function name_field_placeholder_callback() {
5771 + $all_options = get_option('mxchat_options', []);
5772 + $placeholder = isset($all_options['name_field_placeholder'])
5773 + ? $all_options['name_field_placeholder']
5774 + : esc_html__('Enter your name', 'mxchat');
4322 5775
5776 + echo '<input type="text" id="name_field_placeholder" name="name_field_placeholder" value="' . esc_attr($placeholder) . '" style="width: 300px;" />';
5777 + echo '<p class="description">';
5778 + echo esc_html__('Placeholder text for the name field.', 'mxchat');
5779 + echo '</p>';
5780 +}
4323 5781
5782 +
5783 +
4324 5784 public function mxchat_intro_message_callback() {
4325 5785 // Load the entire 'mxchat_options' array
4326 5786 $all_options = get_option('mxchat_options', []);
4327 -
4328 5787 // Retrieve the saved intro message or use the default
4329 5788 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4330 5789 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4331 -
4332 - // Output the textarea with the saved value
5790 + // Output the textarea with the saved value without escaping HTML
4333 5791 ?>
4334 - <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
5792 + <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4335 5793 <p class="description">
4336 - <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
5794 + <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4337 5795 </p>
4338 5796 <?php
4339 5797 }
4340 5798
4341 -
4342 5799 public function mxchat_input_copy_callback() {
4343 5800 // Load the entire 'mxchat_options' array
4344 5801 $all_options = get_option('mxchat_options', []);
4345 5802
@@ -4805,8 +6262,23 @@
4805 6262 '</p>';
4806 6263
4807 6264 }
4808 6265
6266 +public function mxchat_contextual_awareness_callback() {
6267 + // Get value from options array, default to 'off'
6268 + $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
6269 + $checked = ($contextual_awareness === 'on') ? 'checked' : '';
6270 + echo '<label class="toggle-switch">';
6271 + echo sprintf(
6272 + '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
6273 + esc_attr($checked)
6274 + );
6275 + echo '<span class="slider"></span>';
6276 + echo '</label>';
6277 + echo '<p class="description">' .
6278 + esc_html__('Enable contextual awareness to allow the chatbot to understand and reference the current page content. When enabled, the chatbot will have access to the page title, content, and URL for more relevant responses.', 'mxchat') .
6279 + '</p>';
6280 +}
4809 6281
4810 6282
4811 6283 public function mxchat_privacy_toggle_callback() {
4812 6284 // Load from mxchat_options array
@@ -4909,13 +6381,13 @@
4909 6381 // Render the input field
4910 6382 printf(
4911 6383 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4912 6384 esc_attr($popular_question_1),
4913 - esc_attr__('Enter Popular Question 1', 'mxchat')
6385 + esc_attr__('Enter Quick Question 1', 'mxchat')
4914 6386 );
4915 6387
4916 6388 // Add a description for the field
4917 - echo '<p class="description">' . esc_html__('This will be the first popular question in the chatbot.', 'mxchat') . '</p>';
6389 + echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
4918 6390 }
4919 6391
4920 6392
4921 6393 public function mxchat_popular_question_2_callback() {
@@ -4928,13 +6400,13 @@
4928 6400 // Render the input field
4929 6401 printf(
4930 6402 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4931 6403 esc_attr($popular_question_2),
4932 - esc_attr__('Enter Popular Question 2', 'mxchat')
6404 + esc_attr__('Enter Quick Question 2', 'mxchat')
4933 6405 );
4934 6406
4935 6407 // Add a description for the field
4936 - echo '<p class="description">' . esc_html__('This will be the second popular question in the chatbot.', 'mxchat') . '</p>';
6408 + echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
4937 6409 }
4938 6410
4939 6411
4940 6412 public function mxchat_popular_question_3_callback() {
@@ -4947,13 +6419,13 @@
4947 6419 // Render the input field
4948 6420 printf(
4949 6421 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
4950 6422 esc_attr($popular_question_3),
4951 - esc_attr(__('Enter Popular Question 3', 'mxchat'))
6423 + esc_attr(__('Enter Quick Question 3', 'mxchat'))
4952 6424 );
4953 6425
4954 6426 // Add a description for the field
4955 - echo '<p class="description">' . esc_html__('This will be the third popular question in the chatbot.', 'mxchat') . '</p>';
6427 + echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
4956 6428 }
4957 6429
4958 6430 public function mxchat_additional_popular_questions_callback() {
4959 6431 $options = get_option('mxchat_options', []);
@@ -4974,9 +6446,9 @@
4974 6446 <button type="button" class="button mxchat-remove-question"
4975 6447 aria-label="%s">%s</button>
4976 6448 </div>',
4977 6449 esc_attr($question),
4978 - esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
6450 + esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
4979 6451 $index,
4980 6452 esc_attr(__('Remove question', 'mxchat')),
4981 6453 esc_html__('Remove', 'mxchat')
4982 6454 );
@@ -4991,9 +6463,9 @@
4991 6463 data-question-index="0" />
4992 6464 <button type="button" class="button mxchat-remove-question"
4993 6465 aria-label="%s">%s</button>
4994 6466 </div>',
4995 - esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
6467 + esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
4996 6468 esc_attr(__('Remove question', 'mxchat')),
4997 6469 esc_html__('Remove', 'mxchat')
4998 6470 );
4999 6471 }
@@ -5002,9 +6474,9 @@
5002 6474 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5003 6475 esc_attr(__('Add question', 'mxchat')),
5004 6476 esc_html__('Add Question', 'mxchat')
5005 6477 );
5006 - echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
6478 + echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5007 6479 echo '</div>';
5008 6480 }
5009 6481
5010 6482 public function mxchat_brave_api_key_callback() {
@@ -5016,9 +6488,9 @@
5016 6488 $brave_api_key
5017 6489 );
5018 6490 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5019 6491 echo '</div>';
5020 - echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
6492 + echo '<p class="description">' . __('Required for Brave Search integration. Get your API key from Brave Search API.', 'mxchat') . '</p>';
5021 6493 }
5022 6494
5023 6495 public function mxchat_brave_image_count_callback() {
5024 6496 $brave_image_count = isset($this->options['brave_image_count'])
@@ -5103,141 +6575,118 @@
5103 6575 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5104 6576 }
5105 6577
5106 6578 public function mxchat_chat_toolbar_toggle_callback() {
5107 - // Get chat toolbar toggle value with fallback
5108 - $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5109 - $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
6579 + // Get chat toolbar toggle value with fallback
6580 + $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
6581 + $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5110 6582
5111 - // Check if the plugin is activated (paid feature)
5112 - $disabled = $this->is_activated ? '' : 'disabled';
5113 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6583 + // Output the toggle switch
6584 + echo '<label class="toggle-switch">';
6585 + echo sprintf(
6586 + '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
6587 + esc_attr($checked)
6588 + );
6589 + echo '<span class="slider"></span>';
6590 + echo '</label>';
5114 6591
5115 - echo '<div class="' . esc_attr($class) . '">';
6592 + echo '<p class="description">' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '</p>';
6593 +}
5116 6594
5117 - // Output the toggle switch
5118 - echo '<label class="toggle-switch">';
5119 - echo sprintf(
5120 - '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
5121 - esc_attr($checked),
5122 - esc_attr($disabled)
5123 - );
5124 - echo '<span class="slider"></span>';
5125 - echo '</label>';
6595 +/**
6596 + * Callback for PDF upload button toggle setting
6597 + */
6598 +public function mxchat_show_pdf_upload_button_callback() {
6599 + // Get toggle value with fallback
6600 + $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
6601 + $checked = ($show_pdf_button === 'on') ? 'checked' : '';
5126 6602
5127 - // Pro feature overlay
5128 - if (!$this->is_activated) {
5129 - echo '<div class="pro-feature-overlay">';
5130 - echo '<a href="https://mxchat.ai/" target="_blank">';
5131 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5132 - echo '</a>';
5133 - echo '</div>';
5134 - }
6603 + // Output the toggle switch
6604 + echo '<label class="toggle-switch">';
6605 + echo sprintf(
6606 + '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
6607 + esc_attr($checked)
6608 + );
6609 + echo '<span class="slider"></span>';
6610 + echo '</label>';
5135 6611
5136 - echo '</div>';
5137 - echo '<p class="description">' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '</p>';
6612 + echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5138 6613 }
5139 6614
6615 +/**
6616 + * Callback for Word upload button toggle setting
6617 + */
6618 +public function mxchat_show_word_upload_button_callback() {
6619 + // Get toggle value with fallback
6620 + $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
6621 + $checked = ($show_word_button === 'on') ? 'checked' : '';
5140 6622
6623 + // Output the toggle switch
6624 + echo '<label class="toggle-switch">';
6625 + echo sprintf(
6626 + '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
6627 + esc_attr($checked)
6628 + );
6629 + echo '<span class="slider"></span>';
6630 + echo '</label>';
6631 +
6632 + echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
6633 +}
6634 +
5141 6635 public function mxchat_pdf_intent_trigger_text_callback() {
5142 - $disabled = $this->is_activated ? '' : 'disabled';
5143 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5144 6636 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5145 6637
5146 - echo '<div class="' . esc_attr($class) . '">';
5147 6638 echo sprintf(
5148 6639 '<textarea id="pdf_intent_trigger_text"
5149 6640 name="pdf_intent_trigger_text"
5150 6641 rows="3"
5151 6642 cols="50"
5152 - placeholder="%s"
5153 - %s>%s</textarea>',
6643 + placeholder="%s">%s</textarea>',
5154 6644 esc_attr__('Enter trigger text', 'mxchat'),
5155 - esc_attr($disabled),
5156 6645 isset($this->options['pdf_intent_trigger_text'])
5157 6646 ? esc_textarea($this->options['pdf_intent_trigger_text'])
5158 6647 : esc_textarea($default_text)
5159 6648 );
5160 6649 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5161 -
5162 - if (!$this->is_activated) {
5163 - echo '<div class="pro-feature-overlay">';
5164 - echo '<a href="https://mxchat.ai/" target="_blank">';
5165 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5166 - echo '</a>';
5167 - echo '</div>';
5168 - }
5169 - echo '</div>';
5170 6650 }
5171 6651
5172 6652 public function mxchat_pdf_intent_success_text_callback() {
5173 - $disabled = $this->is_activated ? '' : 'disabled';
5174 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5175 6653 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5176 6654
5177 - echo '<div class="' . esc_attr($class) . '">';
5178 6655 echo sprintf(
5179 6656 '<textarea id="pdf_intent_success_text"
5180 6657 name="pdf_intent_success_text"
5181 6658 rows="3"
5182 6659 cols="50"
5183 - placeholder="%s"
5184 - %s>%s</textarea>',
6660 + placeholder="%s">%s</textarea>',
5185 6661 esc_attr__('Enter success text', 'mxchat'),
5186 - esc_attr($disabled),
5187 6662 isset($this->options['pdf_intent_success_text'])
5188 6663 ? esc_textarea($this->options['pdf_intent_success_text'])
5189 6664 : esc_textarea($default_text)
5190 6665 );
5191 6666 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5192 -
5193 - if (!$this->is_activated) {
5194 - echo '<div class="pro-feature-overlay">';
5195 - echo '<a href="https://mxchat.ai/" target="_blank">';
5196 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5197 - echo '</a>';
5198 - echo '</div>';
5199 - }
5200 - echo '</div>';
5201 6667 }
5202 6668
5203 6669 public function mxchat_pdf_intent_error_text_callback() {
5204 - $disabled = $this->is_activated ? '' : 'disabled';
5205 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5206 6670 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5207 6671
5208 - echo '<div class="' . esc_attr($class) . '">';
5209 6672 echo sprintf(
5210 6673 '<textarea id="pdf_intent_error_text"
5211 6674 name="pdf_intent_error_text"
5212 6675 rows="3"
5213 6676 cols="50"
5214 - placeholder="%s"
5215 - %s>%s</textarea>',
6677 + placeholder="%s">%s</textarea>',
5216 6678 esc_attr__('Enter error text', 'mxchat'),
5217 - esc_attr($disabled),
5218 6679 isset($this->options['pdf_intent_error_text'])
5219 6680 ? esc_textarea($this->options['pdf_intent_error_text'])
5220 6681 : esc_textarea($default_text)
5221 6682 );
5222 6683 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5223 -
5224 - if (!$this->is_activated) {
5225 - echo '<div class="pro-feature-overlay">';
5226 - echo '<a href="https://mxchat.ai/" target="_blank">';
5227 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5228 - echo '</a>';
5229 - echo '</div>';
5230 - }
5231 - echo '</div>';
5232 6684 }
5233 6685
5234 6686 public function mxchat_pdf_max_pages_callback() {
5235 - $disabled = $this->is_activated ? '' : 'disabled';
5236 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5237 6687 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5238 6688
5239 - echo '<div class="' . esc_attr($class) . '">';
5240 6689 echo sprintf(
5241 6690 '<input type="range"
5242 6691 id="pdf_max_pages"
5243 6692 name="pdf_max_pages"
@@ -5243,37 +6692,24 @@
5243 6692 name="pdf_max_pages"
5244 6693 min="1"
5245 6694 max="69"
5246 6695 value="%d"
5247 - %s
5248 6696 class="range-slider" />',
5249 - esc_attr($max_pages),
5250 - esc_attr($disabled)
6697 + esc_attr($max_pages)
5251 6698 );
5252 6699 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5253 6700 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5254 -
5255 - if (!$this->is_activated) {
5256 - echo '<div class="pro-feature-overlay">';
5257 - echo '<a href="https://mxchat.ai/" target="_blank">';
5258 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5259 - echo '</a>';
5260 - echo '</div>';
5261 - }
5262 - echo '</div>';
5263 6701 }
5264 6702
5265 6703 public function mxchat_live_agent_status_callback() {
5266 - $disabled = $this->is_activated ? '' : 'disabled';
5267 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5268 - $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off';
6704 + // Always get fresh options instead of using cached $this->options
6705 + $fresh_options = get_option('mxchat_options');
6706 + $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
5269 6707
5270 - echo '<div class="' . esc_attr($class) . '">';
5271 6708 echo '<label class="toggle-switch">';
5272 6709 echo sprintf(
5273 - '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s %s />',
5274 - checked($status, 'on', false),
5275 - esc_attr($disabled)
6710 + '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
6711 + checked($status, 'on', false)
5276 6712 );
5277 6713 echo '<span class="slider"></span>';
5278 6714 echo '</label>';
5279 6715 echo '<label for="live_agent_status" class="mxchat-status-label">';
@@ -5278,98 +6714,83 @@
5278 6714 echo '</label>';
5279 6715 echo '<label for="live_agent_status" class="mxchat-status-label">';
5280 6716 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5281 6717 echo '</label>';
5282 -
5283 - if (!$this->is_activated) {
5284 - echo '<div class="pro-feature-overlay">';
5285 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5286 - echo '</div>';
5287 - }
5288 - echo '</div>';
5289 6718 }
5290 6719
5291 -
5292 -// Away Message Callback
5293 6720 public function mxchat_live_agent_away_message_callback() {
5294 - $disabled = $this->is_activated ? '' : 'disabled';
5295 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5296 6721 $message = isset($this->options['live_agent_away_message'])
5297 6722 ? $this->options['live_agent_away_message']
5298 6723 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5299 6724
5300 - echo '<div class="' . esc_attr($class) . '">';
5301 6725 printf(
5302 - '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50" %s>%s</textarea>',
5303 - esc_attr($disabled),
6726 + '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
5304 6727 esc_textarea($message)
5305 6728 );
5306 6729 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5307 -
5308 - if (!$this->is_activated) {
5309 - echo '<div class="pro-feature-overlay">';
5310 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5311 - echo '</div>';
5312 - }
5313 - echo '</div>';
5314 6730 }
5315 6731
5316 6732 public function mxchat_live_agent_notification_message_callback() {
5317 - $disabled = $this->is_activated ? '' : 'disabled';
5318 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5319 6733 $message = isset($this->options['live_agent_notification_message'])
5320 6734 ? $this->options['live_agent_notification_message']
5321 6735 : __('Live agent has been notified.', 'mxchat');
5322 6736
5323 - echo '<div class="' . esc_attr($class) . '">';
5324 6737 printf(
5325 - '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50" %s>%s</textarea>',
5326 - esc_attr($disabled),
6738 + '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
5327 6739 esc_textarea($message)
5328 6740 );
5329 6741 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5330 -
5331 - if (!$this->is_activated) {
5332 - echo '<div class="pro-feature-overlay">';
5333 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5334 - echo '</div>';
5335 - }
5336 - echo '</div>';
5337 6742 }
5338 6743
5339 6744 public function mxchat_live_agent_webhook_url_callback() {
5340 - $disabled = $this->is_activated ? '' : 'disabled';
5341 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5342 6745 $webhook_url = isset($this->options['live_agent_webhook_url'])
5343 6746 ? esc_url($this->options['live_agent_webhook_url'])
5344 6747 : esc_url(get_option('live_agent_webhook_url', ''));
5345 6748
5346 - echo '<div class="' . esc_attr($class) . '">';
5347 6749 printf(
5348 - '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" %s />',
5349 - $webhook_url,
5350 - esc_attr($disabled)
6750 + '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
6751 + $webhook_url
5351 6752 );
5352 6753 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5353 6754 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
6755 +}
5354 6756
5355 - if (!$this->is_activated) {
5356 - echo '<div class="pro-feature-overlay">';
5357 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5358 - echo '</div>';
5359 - }
5360 - echo '</div>';
6757 +public function mxchat_live_agent_secret_key_callback() {
6758 + printf(
6759 + '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
6760 + isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
6761 + );
6762 + echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6763 + echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5361 6764 }
5362 6765
6766 +public function mxchat_live_agent_bot_token_callback() {
6767 + printf(
6768 + '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
6769 + isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
6770 + );
6771 + echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6772 + echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
6773 +}
5363 6774
6775 +public function mxchat_live_agent_user_ids_callback() {
6776 + $user_ids = isset($this->options['live_agent_user_ids'])
6777 + ? esc_textarea($this->options['live_agent_user_ids'])
6778 + : '';
6779 +
6780 + printf(
6781 + '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
6782 + $user_ids
6783 + );
6784 + echo '<p class="description">' . esc_html__('Enter Slack User IDs of agents who should be automatically invited to chat channels (one per line). Find user IDs in Slack profiles under "More" → "Copy member ID". Example: U1234567890', 'mxchat') . '</p>';
6785 +}
6786 +
5364 6787 public function mxchat_similarity_threshold_callback() {
5365 6788 // Load from mxchat_options array
5366 6789 $options = get_option('mxchat_options', []);
5367 6790
5368 - // Get value with backwards compatibility
5369 - $threshold = isset($options['similarity_threshold'])
5370 - ? $options['similarity_threshold']
5371 - : get_option('mxchat_similarity_threshold', 80);
6791 + // Get value from options array with default of 80
6792 + $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
5372 6793
5373 6794 echo '<div class="slider-container">';
5374 6795 echo sprintf(
5375 6796 '<input type="range"
@@ -5374,9 +6795,9 @@
5374 6795 echo sprintf(
5375 6796 '<input type="range"
5376 6797 id="similarity_threshold"
5377 6798 name="similarity_threshold"
5378 - min="70"
6799 + min="20"
5379 6800 max="85"
5380 6801 step="1"
5381 6802 value="%s"
5382 6803 class="range-slider" />',
@@ -5386,186 +6807,198 @@
5386 6807 '<span id="threshold_value" class="range-value">%s</span>',
5387 6808 esc_html($threshold)
5388 6809 );
5389 6810 echo '</div>';
5390 -
5391 6811 echo '<p class="description">';
5392 - echo esc_html__('Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge.', 'mxchat');
6812 + echo esc_html__('Adjust similarity threshold for optimal content matching. Too high may limit knowledge retrieval. We highly recommend using the MxChat Debugger found on the left side of your website when logged in as admin while testing the bot.', 'mxchat');
5393 6813 echo '</p>';
5394 6814 }
5395 6815
6816 +public function mxchat_enqueue_admin_assets() {
6817 + // Get plugin version
6818 + $version = MXCHAT_VERSION;
5396 6819
6820 + // Use file modification time for development (remove in production)
6821 + if (defined('WP_DEBUG') && WP_DEBUG) {
6822 + $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
6823 + }
5397 6824
5398 -public function mxchat_live_agent_secret_key_callback() {
5399 - $disabled = $this->is_activated ? '' : 'disabled';
5400 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6825 + $current_page = isset($_GET['page']) ? $_GET['page'] : '';
6826 + $plugin_url = plugin_dir_url(__FILE__) . '../';
5401 6827
5402 - echo '<div class="' . esc_attr($class) . '">';
5403 - printf(
5404 - '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" %s />',
5405 - isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
5406 - esc_attr($disabled)
5407 - );
5408 - echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5409 - echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
6828 + // Only load on MxChat pages
6829 + if (strpos($current_page, 'mxchat') === false) {
6830 + return;
6831 + }
5410 6832
5411 - if (!$this->is_activated) {
5412 - echo '<div class="pro-feature-overlay">';
5413 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5414 - echo '</div>';
5415 - }
5416 - echo '</div>';
6833 + // Always load these on all MxChat pages
6834 + $this->enqueue_core_admin_assets($plugin_url, $version);
6835 + $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
6836 + $this->localize_admin_scripts($current_page);
5417 6837 }
6838 +private function enqueue_core_admin_assets($plugin_url, $version) {
6839 + // Core admin styles
6840 + wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
6841 + wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
5418 6842
5419 -public function mxchat_live_agent_bot_token_callback() {
5420 - $disabled = $this->is_activated ? '' : 'disabled';
5421 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5422 -
5423 - echo '<div class="' . esc_attr($class) . '">';
5424 - printf(
5425 - '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" %s />',
5426 - isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
5427 - esc_attr($disabled)
5428 - );
5429 - echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5430 - echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5431 -
5432 - if (!$this->is_activated) {
5433 - echo '<div class="pro-feature-overlay">';
5434 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5435 - echo '</div>';
5436 - }
5437 - echo '</div>';
6843 + // Core admin scripts
6844 + wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
5438 6845 }
6846 +private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
6847 + switch ($current_page) {
6848 + case 'mxchat-prompts':
6849 + // Knowledge processing page assets
6850 + wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
6851 + wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
6852 + // Add the knowledge processing script
6853 + wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
6854 + break;
5439 6855
5440 -public function mxchat_enqueue_admin_assets() {
5441 - wp_enqueue_style('wp-color-picker');
6856 + case 'mxchat-transcripts':
6857 + wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
6858 + wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
6859 + break;
5442 6860
5443 - // Get the plugin version or file modification time for cache busting
5444 - $plugin_version = '2.0.3'; // Replace this with your plugin's version
6861 + case 'mxchat-actions':
6862 + wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
6863 + break;
5445 6864
5446 - // File paths
5447 - $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5448 - $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5449 - $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5450 - $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5451 - $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5452 - $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
6865 + case 'mxchat-activation':
6866 + wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
6867 + break;
6868 + default:
6869 + wp_enqueue_script(
6870 + 'mxchat-test-streaming-js',
6871 + $plugin_url . 'js/mxchat-test-streaming.js',
6872 + ['jquery'],
6873 + $version,
6874 + true
6875 + );
5453 6876
5454 - // Check if files exist and get modification times
5455 - $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5456 - $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5457 - $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5458 - $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5459 - $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5460 - $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5461 -
5462 - // Enqueue scripts and styles with corrected paths
5463 - wp_enqueue_script(
5464 - 'mxchat-color-picker',
5465 - plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
5466 - array('wp-color-picker'),
5467 - $color_picker_version,
5468 - true
5469 - );
5470 -
5471 - wp_enqueue_script(
5472 - 'mxchat-embedding-check',
5473 - plugin_dir_url(__FILE__) . '../js/embedding-check.js',
5474 - array(),
5475 - $embedding_check_version,
5476 - true
5477 - );
5478 -
5479 - wp_enqueue_script(
5480 - 'mxchat-transcripts-js',
5481 - plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5482 - array('jquery'),
5483 - $transcripts_js_version,
5484 - true
5485 - );
5486 -
5487 - wp_enqueue_script(
5488 - 'mxchat-admin-js',
5489 - plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5490 - array('jquery'),
5491 - $plugin_version,
5492 - true
5493 - );
5494 -
5495 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
6877 + wp_localize_script('mxchat-test-streaming-js', 'mxchatTestStreamingAjax', [
6878 + 'ajax_url' => admin_url('admin-ajax.php'),
6879 + 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce')
6880 + ]);
6881 + break;
6882 + }
6883 +}
6884 +private function localize_admin_scripts($current_page) {
6885 + // Base localization data for main admin script
6886 + $base_data = array(
5496 6887 'ajax_url' => admin_url('admin-ajax.php'),
6888 + 'nonce' => wp_create_nonce('mxchat_status_nonce'),
6889 + 'admin_url' => admin_url(),
5497 6890 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5498 6891 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5499 6892 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5500 - ));
6893 + 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
6894 + 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6895 + 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
6896 + 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
6897 + 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6898 + 'fetch_openrouter_models_nonce' => wp_create_nonce('mxchat_fetch_openrouter_models'), // ADD THIS LINE
6899 + 'is_activated' => $this->is_activated ? '1' : '0',
6900 + 'status_refresh_interval' => 5000,
6901 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
6902 + 'ajaxurl' => admin_url('admin-ajax.php')
6903 + );
5501 6904
5502 - // Enqueue the admin CSS
5503 - wp_enqueue_style(
5504 - 'mxchat-admin-css',
5505 - plugin_dir_url(__FILE__) . '../css/admin-style.css',
5506 - array(),
5507 - $admin_css_version
5508 - );
6905 + // Localize main admin script with base data
6906 + wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
5509 6907
5510 - wp_enqueue_style(
5511 - 'mxchat-knowledge-css',
5512 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5513 - array(),
5514 - $knowledge_css_version
5515 - );
6908 + // Page-specific localizations
6909 + $this->localize_page_specific_scripts($current_page);
6910 +}
6911 +private function localize_page_specific_scripts($current_page) {
6912 + switch ($current_page) {
6913 + case 'mxchat-prompts':
6914 + // Status updater localization
6915 + wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
6916 + 'ajax_url' => admin_url('admin-ajax.php'),
6917 + 'nonce' => wp_create_nonce('mxchat_status_nonce')
6918 + ));
5516 6919
6920 + // Content selector localization
6921 + wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
6922 + 'ajaxurl' => admin_url('admin-ajax.php'),
6923 + 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
6924 + 'i18n' => array(
6925 + 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
6926 + 'selectAll' => __('Select All', 'mxchat'),
6927 + 'process' => __('Process Selected', 'mxchat'),
6928 + 'cancel' => __('Cancel', 'mxchat'),
6929 + 'noResults' => __('No content found.', 'mxchat')
6930 + )
6931 + ));
5517 6932
5518 - wp_enqueue_style(
5519 - 'mxchat-intent-css',
5520 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5521 - array(),
5522 - $intent_css_version
5523 - );
6933 + wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
6934 + 'ajax_url' => admin_url('admin-ajax.php'),
6935 + 'status_nonce' => wp_create_nonce('mxchat_status_nonce'),
6936 + 'queue_nonce' => wp_create_nonce('mxchat_queue_nonce'),
6937 + 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
6938 + 'settings_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
6939 + 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
6940 + 'admin_url' => admin_url(),
6941 + 'ajaxurl' => admin_url('admin-ajax.php'),
6942 + 'status_refresh_interval' => 2000
6943 + ));
6944 + break;
5524 6945
5525 - // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5526 - wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5527 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
5528 - 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5529 - ) );
6946 + case 'mxchat-activation':
6947 + wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
6948 + 'ajax_url' => admin_url('admin-ajax.php'),
6949 + 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
6950 + ));
6951 + break;
5530 6952
5531 - // Localize the script for color picker and settings
5532 - wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6953 + case 'mxchat-settings':
6954 + default:
6955 + // Color picker and settings localization
6956 + wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6957 + 'ajax_url' => admin_url('admin-ajax.php'),
6958 + 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
6959 + 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
6960 + 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
6961 + 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
6962 + 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
6963 + 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
6964 + 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
6965 + 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
6966 + 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
6967 + 'icon_color' => $this->options['icon_color'] ?? '#fff',
6968 + 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
6969 + 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
6970 + 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
6971 + 'loops_api_key' => $this->options['loops_api_key'] ?? '',
6972 + 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
6973 + 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
6974 + 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
6975 + 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
6976 + 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
6977 + 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
6978 + 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
6979 + 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
6980 + 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
6981 + 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
6982 + 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
6983 + 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
6984 + 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
6985 + 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
6986 + 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
6987 + 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
6988 + 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
6989 + 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6990 + ));
6991 + break;
6992 + }
6993 +
6994 + // Additional localization that was in the original code
6995 + wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
5533 6996 'ajax_url' => admin_url('admin-ajax.php'),
5534 - 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5535 - 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5536 - 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5537 - 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5538 - 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5539 - 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5540 - 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5541 - 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5542 - 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5543 - 'icon_color' => $this->options['icon_color'] ?? '#fff',
5544 - 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5545 - 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5546 - 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5547 - 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5548 - 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5549 - 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5550 - 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5551 - 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
5552 - 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
5553 - 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
5554 - 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5555 - 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5556 - 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5557 - 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5558 - 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5559 - 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5560 - 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5561 - 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5562 - 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5563 - 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6997 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5564 6998 ));
5565 6999 }
5566 7000
5567 -
5568 7001 public function mxchat_sanitize($input) {
5569 7002 $new_input = array();
5570 7003
5571 7004 if (isset($input['api_key'])) {
@@ -5573,9 +7006,9 @@
5573 7006 }
5574 7007
5575 7008 if (isset($input['similarity_threshold'])) {
5576 7009 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5577 - $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
7010 + $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5578 7011 }
5579 7012
5580 7013 if (isset($input['xai_api_key'])) {
5581 7014 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
@@ -5584,12 +7017,24 @@
5584 7017 if (isset($input['claude_api_key'])) {
5585 7018 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5586 7019 }
5587 7020
7021 + if (isset($input['enable_streaming_toggle'])) {
7022 + $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
7023 + } else {
7024 + // If checkbox not checked, it won't be in $input, so set to 'off'
7025 + $new_input['enable_streaming_toggle'] = 'off';
7026 + }
7027 +
7028 +
5588 7029 if (isset($input['deepseek_api_key'])) {
5589 7030 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5590 7031 }
5591 7032
7033 + if (isset($input['gemini_api_key'])) {
7034 + $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
7035 + }
7036 +
5592 7037 if (isset($input['enable_woocommerce_integration'])) {
5593 7038 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5594 7039 }
5595 7040
@@ -5622,12 +7067,20 @@
5622 7067 if (isset($input['append_to_body'])) {
5623 7068 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5624 7069 }
5625 7070
7071 + if (isset($input['contextual_awareness_toggle'])) {
7072 + $new_input['contextual_awareness_toggle'] = $input['contextual_awareness_toggle'] === 'on' ? 'on' : 'off';
7073 +}
7074 +
5626 7075 if (isset($input['top_bar_title'])) {
5627 7076 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5628 7077 }
5629 7078
7079 + if (isset($input['ai_agent_text'])) {
7080 + $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
7081 + }
7082 +
5630 7083 if (isset($input['enable_email_block'])) {
5631 7084 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5632 7085 }
5633 7086
@@ -5637,11 +7090,20 @@
5637 7090 }
5638 7091 if (isset($input['email_blocker_button_text'])) {
5639 7092 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5640 7093 }
5641 -
7094 + // Sanitize name field toggle
7095 + if (isset($input['enable_name_field'])) {
7096 + $new_input['enable_name_field'] = ($input['enable_name_field'] === 'on') ? 'on' : 'off';
7097 + } else {
7098 + $new_input['enable_name_field'] = 'off';
7099 + }
7100 + // Sanitize name field placeholder
7101 + if (isset($input['name_field_placeholder'])) {
7102 + $new_input['name_field_placeholder'] = sanitize_text_field($input['name_field_placeholder']);
7103 + }
5642 7104 if (isset($input['intro_message'])) {
5643 - $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
7105 + $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5644 7106 }
5645 7107
5646 7108 if (isset($input['input_copy'])) {
5647 7109 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
@@ -5650,47 +7112,99 @@
5650 7112 if (isset($input['rate_limit_message'])) {
5651 7113 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5652 7114 }
5653 7115
5654 - if (isset($input['rate_limit_logged_out'])) {
5655 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5656 - $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
7116 +// Handle the new rate limits format
7117 +if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
7118 + $new_input['rate_limits'] = array();
7119 + $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
7120 + $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
5657 7121
5658 - if (in_array($rate_limit, $allowed_limits, true)) {
5659 - $new_input['rate_limit_logged_out'] = $rate_limit;
5660 - } else {
5661 - $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
7122 + foreach ($input['rate_limits'] as $role_id => $settings) {
7123 + $new_input['rate_limits'][$role_id] = array();
7124 +
7125 + // Sanitize limit
7126 + if (isset($settings['limit'])) {
7127 + $limit = sanitize_text_field($settings['limit']);
7128 + if (in_array($limit, $allowed_limits, true)) {
7129 + $new_input['rate_limits'][$role_id]['limit'] = $limit;
7130 + } else {
7131 + $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
7132 + }
5662 7133 }
5663 - }
5664 7134
5665 - // Handle role rate limits
5666 - if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) {
5667 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5668 - $new_input['role_rate_limits'] = array();
5669 -
5670 - foreach ($input['role_rate_limits'] as $role_id => $rate_limit) {
5671 - $rate_limit = sanitize_text_field($rate_limit);
5672 - if (in_array($rate_limit, $allowed_limits, true)) {
5673 - $new_input['role_rate_limits'][$role_id] = $rate_limit;
7135 + // Sanitize timeframe
7136 + if (isset($settings['timeframe'])) {
7137 + $timeframe = sanitize_text_field($settings['timeframe']);
7138 + if (in_array($timeframe, $allowed_timeframes, true)) {
7139 + $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
5674 7140 } else {
5675 - $new_input['role_rate_limits'][$role_id] = '100'; // Default
7141 + $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
5676 7142 }
5677 7143 }
7144 +
7145 + // Sanitize message
7146 + if (isset($settings['message'])) {
7147 + $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
7148 + }
5678 7149 }
7150 +}
5679 7151
5680 7152 if (isset($input['pre_chat_message'])) {
5681 7153 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5682 7154 }
5683 7155
5684 - if (isset($input['model'])) {
7156 + if (isset($input['voyage_api_key'])) {
7157 + $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
7158 + }
7159 +
7160 + // Add to your sanitize function
7161 + if (isset($input['embedding_model'])) {
5685 7162 $allowed_models = array(
5686 - 'grok-beta',
7163 + 'text-embedding-ada-002',
7164 + 'text-embedding-3-small',
7165 + 'text-embedding-3-large',
7166 + 'voyage-3-large',
7167 + 'gemini-embedding-exp-03-07'
7168 + );
7169 + if (in_array($input['embedding_model'], $allowed_models)) {
7170 + $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
7171 + }
7172 + }
7173 +
7174 +if (isset($input['model'])) {
7175 + if ($input['model'] === 'openrouter') {
7176 + $new_input['model'] = 'openrouter';
7177 + } else {
7178 + $allowed_models = array(
7179 + 'gemini-2.0-flash',
7180 + 'gemini-2.0-flash-lite',
7181 + 'gemini-1.5-pro',
7182 + 'gemini-1.5-flash',
7183 + 'grok-4-0709',
7184 + 'grok-4-1-fast-reasoning',
7185 + 'grok-4-1-fast-non-reasoning',
7186 + 'grok-3-beta',
7187 + 'grok-3-fast-beta',
7188 + 'grok-3-mini-beta',
7189 + 'grok-3-mini-fast-beta',
5687 7190 'grok-2',
5688 7191 'deepseek-chat',
5689 - 'claude-3-5-sonnet-20241022',
7192 + 'claude-sonnet-4-5-20250929',
7193 + 'claude-opus-4-1-20250805',
7194 + 'claude-haiku-4-5-20251001',
7195 + 'claude-opus-4-20250514',
7196 + 'claude-sonnet-4-20250514',
7197 + 'claude-3-7-sonnet-20250219',
7198 + // REMOVED: 'claude-3-5-sonnet-20241022',
5690 7199 'claude-3-opus-20240229',
5691 7200 'claude-3-sonnet-20240229',
5692 7201 'claude-3-haiku-20240307',
7202 + 'gpt-5.1-2025-11-13',
7203 + 'gpt-5',
7204 + 'gpt-5-mini',
7205 + 'gpt-5-nano',
7206 + 'gpt-4.1-2025-04-14',
5693 7207 'gpt-4o',
5694 7208 'gpt-4o-mini',
5695 7209 'gpt-4-turbo',
5696 7210 'gpt-4',
@@ -5695,13 +7209,33 @@
5695 7209 'gpt-4-turbo',
5696 7210 'gpt-4',
5697 7211 'gpt-3.5-turbo',
5698 7212 );
7213 +
5699 7214 if (in_array($input['model'], $allowed_models)) {
5700 7215 $new_input['model'] = sanitize_text_field($input['model']);
7216 + } else {
7217 + // Fallback for any deprecated model
7218 + $new_input['model'] = 'claude-3-7-sonnet-20250219';
5701 7219 }
5702 7220 }
7221 +}
7222 +
7223 +if (isset($input['openrouter_selected_model'])) {
7224 + // Just sanitize it, don't validate against a whitelist
7225 + $new_input['openrouter_selected_model'] = sanitize_text_field($input['openrouter_selected_model']);
7226 +}
5703 7227
7228 +// ADD THIS:
7229 +if (isset($input['openrouter_selected_model_name'])) {
7230 + $new_input['openrouter_selected_model_name'] = sanitize_text_field($input['openrouter_selected_model_name']);
7231 +}
7232 +
7233 + if (isset($input['openrouter_api_key'])) {
7234 + $new_input['openrouter_api_key'] = sanitize_text_field($input['openrouter_api_key']);
7235 + }
7236 +
7237 +
5704 7238 if (isset($input['close_button_color'])) {
5705 7239 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5706 7240 }
5707 7241
@@ -5756,8 +7290,12 @@
5756 7290 if (isset($input['top_bar_bg_color'])) {
5757 7291 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5758 7292 }
5759 7293
7294 + if (isset($input['quick_questions_toggle_color'])) {
7295 + $new_input['quick_questions_toggle_color'] = sanitize_hex_color($input['quick_questions_toggle_color']);
7296 + }
7297 +
5760 7298 if (isset($input['send_button_font_color'])) {
5761 7299 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5762 7300 }
5763 7301
@@ -5815,15 +7353,14 @@
5815 7353 if (isset($input['loops_mailing_list'])) {
5816 7354 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5817 7355 }
5818 7356
5819 - // Sanitize Triggered Phrase Response
7357 +// Sanitize Triggered Phrase Response
5820 7358 if (isset($input['triggered_phrase_response'])) {
5821 7359 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5822 7360 }
5823 -
5824 7361 if (isset($input['email_capture_response'])) {
5825 - $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
7362 + $new_input['email_capture_response'] = wp_kses_post($input['email_capture_response']);
5826 7363 }
5827 7364
5828 7365 // Sanitize Brave Search Settings
5829 7366 if (isset($input['brave_api_key'])) {
@@ -5856,8 +7393,22 @@
5856 7393 if (isset($input['chat_toolbar_toggle'])) {
5857 7394 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5858 7395 }
5859 7396
7397 + // Sanitize PDF upload button toggle
7398 + if (isset($input['show_pdf_upload_button'])) {
7399 + $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
7400 + } else {
7401 + $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
7402 + }
7403 +
7404 + // Sanitize Word upload button toggle
7405 + if (isset($input['show_word_upload_button'])) {
7406 + $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
7407 + } else {
7408 + $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
7409 + }
7410 +
5860 7411 if (isset($input['pdf_intent_trigger_text'])) {
5861 7412 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5862 7413 }
5863 7414
@@ -5887,8 +7438,12 @@
5887 7438 if (isset($input['live_agent_bot_token'])) {
5888 7439 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
5889 7440 }
5890 7441
7442 + if (isset($input['live_agent_user_ids'])) {
7443 + $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
7444 + }
7445 +
5891 7446 if (isset($input['live_agent_status'])) {
5892 7447 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
5893 7448 }
5894 7449 if (isset($input['live_agent_away_message'])) {
@@ -5911,62 +7466,10 @@
5911 7466 }
5912 7467
5913 7468
5914 7469
5915 -private static function mxchat_extract_main_content($html) {
5916 - if (empty($html)) {
5917 - //error_log('mxchat_extract_main_content: Empty HTML content received');
5918 - return '';
5919 - }
5920 7470
5921 - try {
5922 - $dom = new DOMDocument;
5923 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
5924 7471
5925 - // Simple load of HTML, with @ to suppress warnings
5926 - @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
5927 -
5928 - $xpath = new DOMXPath($dom);
5929 -
5930 - // Simplified selectors focusing on common content areas
5931 - $selectors = [
5932 - '//article',
5933 - '//*[@id="content"]',
5934 - '//*[@class="entry-content"]',
5935 - '//main'
5936 - ];
5937 -
5938 - foreach ($selectors as $selector) {
5939 - $nodes = $xpath->query($selector);
5940 - if ($nodes && $nodes->length > 0) {
5941 - $content = '';
5942 - foreach ($nodes as $node) {
5943 - $content .= $dom->saveHTML($node);
5944 - }
5945 - if (!empty($content)) {
5946 - return $content;
5947 - }
5948 - }
5949 - }
5950 -
5951 - // Fallback: Return the entire body content if no specific selector matches
5952 - $body = $dom->getElementsByTagName('body');
5953 - if ($body->length > 0) {
5954 - return $dom->saveHTML($body->item(0));
5955 - }
5956 -
5957 - // Last resort: return the original HTML
5958 - return $html;
5959 -
5960 - } catch (Exception $e) {
5961 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
5962 - return $html; // Return original HTML if parsing fails
5963 - } finally {
5964 - libxml_clear_errors();
5965 - }
5966 -}
5967 -
5968 -
5969 7472 private function mxchat_fetch_loops_mailing_lists($api_key) {
5970 7473 $url = 'https://app.loops.so/api/v1/lists';
5971 7474 $response = wp_remote_get($url, array(
5972 7475 'headers' => array(
@@ -5984,12 +7487,8 @@
5984 7487
5985 7488 return isset($lists) && is_array($lists) ? $lists : array();
5986 7489 }
5987 7490
5988 -
5989 -
5990 -
5991 -
5992 7491 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
5993 7492 if (empty($vec1) || empty($vec2)) {
5994 7493 return 0.0;
5995 7494 }
@@ -6010,8 +7509,441 @@
6010 7509 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
6011 7510 }
6012 7511 }
6013 7512
7513 +/**
7514 + * Validates nonce and deletes all chat prompts
7515 + */
7516 +public function mxchat_handle_delete_all_prompts() {
7517 + //error_log('=== DELETE ALL DEBUG START ===');
7518 +
7519 + // Verify nonce
7520 + if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
7521 + //error_log('DEBUG: Nonce verification failed');
7522 + wp_die(__('Nonce verification failed.', 'mxchat'));
7523 + }
7524 +
7525 + // Check permissions
7526 + if (!current_user_can('manage_options')) {
7527 + //error_log('DEBUG: Permission check failed');
7528 + wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
7529 + }
7530 +
7531 + // Get bot_id from POST data
7532 + $bot_id = isset($_POST['bot_id']) ? sanitize_text_field($_POST['bot_id']) : 'default';
7533 + //error_log('DEBUG: Bot ID from POST: ' . $bot_id);
7534 +
7535 + $success = true;
7536 + $error_messages = array();
7537 +
7538 + // Get bot-specific Pinecone configuration
7539 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
7540 + $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($bot_id);
7541 +
7542 + //error_log('DEBUG: Pinecone options retrieved:');
7543 + //error_log(' - Use Pinecone: ' . ($pinecone_options['mxchat_use_pinecone'] ?? 'NOT SET'));
7544 + //error_log(' - API Key: ' . (empty($pinecone_options['mxchat_pinecone_api_key']) ? 'EMPTY' : 'SET'));
7545 + //error_log(' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
7546 +
7547 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
7548 +
7549 + if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
7550 + //error_log('[MXCHAT-DELETE] Pinecone is enabled for bot ' . $bot_id . ' - deleting all from Pinecone');
7551 +
7552 + // Delete from bot-specific Pinecone index
7553 + $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
7554 +
7555 + //error_log('DEBUG: Delete all result: ' . ($result['success'] ? 'SUCCESS' : 'FAILED'));
7556 + if (!$result['success']) {
7557 + //error_log('DEBUG: Delete all error: ' . $result['message']);
7558 + }
7559 +
7560 + if (!$result['success']) {
7561 + $success = false;
7562 + $error_messages[] = $result['message'];
7563 + }
7564 +
7565 + // No cache clearing needed since we removed caching
7566 +
7567 + } else {
7568 + //error_log('[MXCHAT-DELETE] Pinecone not enabled for bot ' . $bot_id . ' - deleting from WordPress database');
7569 +
7570 + // Delete from WordPress database
7571 + global $wpdb;
7572 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
7573 +
7574 + // If multi-bot is active and we have a specific bot, delete only that bot's content
7575 + if ($bot_id !== 'default' && class_exists('MxChat_Multi_Bot_Manager')) {
7576 + $result = $wpdb->delete(
7577 + $table_name,
7578 + array('bot_id' => $bot_id),
7579 + array('%s')
7580 + );
7581 + } else {
7582 + // Delete all content for default bot
7583 + $result = $wpdb->query("DELETE FROM {$table_name}");
7584 + }
7585 +
7586 + if ($result === false) {
7587 + $success = false;
7588 + $error_messages[] = 'Failed to delete from WordPress database';
7589 + }
7590 + }
7591 +
7592 + // No cache clearing needed since we removed caching
7593 +
7594 + //error_log('=== DELETE ALL DEBUG END ===');
7595 +
7596 + // Redirect back with a success message and bot_id
7597 + $redirect_url = add_query_arg(array(
7598 + 'page' => 'mxchat-prompts',
7599 + 'bot_id' => $bot_id,
7600 + 'all_deleted' => $success ? 'true' : 'false'
7601 + ), admin_url('admin.php'));
7602 +
7603 + wp_safe_redirect($redirect_url);
7604 + exit;
7605 +}
7606 +
7607 +
7608 +
7609 + /**
7610 + * Handles deletion prompt with nonce validation
7611 + */
7612 + public function mxchat_handle_delete_prompt() {
7613 + // Sanitize and validate nonce
7614 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
7615 + if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
7616 + wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
7617 + }
7618 +
7619 + // Check permissions
7620 + if (!current_user_can('manage_options')) {
7621 + wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
7622 + }
7623 +
7624 + // Get ID and source parameters
7625 + $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
7626 + $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
7627 +
7628 + if (empty($id)) {
7629 + wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
7630 + }
7631 +
7632 + $success = false;
7633 + $error_message = '';
7634 +
7635 + // Check if Pinecone is enabled and determine source automatically if not specified
7636 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
7637 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
7638 +
7639 + // If source is not specified, determine based on Pinecone configuration
7640 + if (empty($source)) {
7641 + $source = $use_pinecone ? 'pinecone' : 'wordpress';
7642 + }
7643 +
7644 + if ($source === 'pinecone' || $use_pinecone) {
7645 + //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
7646 +
7647 + // Handle Pinecone deletion
7648 + if (empty($pinecone_options['mxchat_pinecone_host']) ||
7649 + empty($pinecone_options['mxchat_pinecone_api_key'])) {
7650 + wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
7651 + }
7652 +
7653 + // Delete from Pinecone using the vector ID directly
7654 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
7655 + $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
7656 + $id,
7657 + $pinecone_options['mxchat_pinecone_api_key'],
7658 + $pinecone_options['mxchat_pinecone_host']
7659 + );
7660 + if ($result['success']) {
7661 + $success = true;
7662 +
7663 + // Remove from vector cache
7664 + $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
7665 + $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
7666 +
7667 + set_transient('mxchat_admin_notice_success',
7668 + esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
7669 + } else {
7670 + $error_message = $result['message'];
7671 + set_transient('mxchat_admin_notice_error',
7672 + esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
7673 + }
7674 + } else {
7675 + //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
7676 +
7677 + // Handle WordPress database deletion
7678 + global $wpdb;
7679 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
7680 +
7681 + // Clear cache and delete prompt
7682 + wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
7683 +
7684 + $result = $wpdb->delete(
7685 + $table_name,
7686 + array('id' => intval($id)),
7687 + array('%d')
7688 + );
7689 +
7690 + if ($result !== false) {
7691 + $success = true;
7692 + set_transient('mxchat_admin_notice_success',
7693 + esc_html__('Entry deleted successfully.', 'mxchat'), 30);
7694 + } else {
7695 + set_transient('mxchat_admin_notice_error',
7696 + esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
7697 + }
7698 + }
7699 +
7700 + // Redirect back to the prompts page
7701 + wp_safe_redirect(add_query_arg(
7702 + array(
7703 + 'page' => 'mxchat-prompts',
7704 + 'deleted' => $success ? 'true' : 'false'
7705 + ),
7706 + admin_url('admin.php')
7707 + ));
7708 + exit;
7709 + }
7710 +
7711 +
7712 +
7713 +
7714 + /**
7715 + * Averages multiple vectors into a single vector
7716 + */
7717 + private function mxchat_average_vectors($vectors) {
7718 + $vector_length = count($vectors[0]);
7719 + $sum_vector = array_fill(0, $vector_length, 0);
7720 +
7721 + foreach ($vectors as $vector) {
7722 + for ($i = 0; $i < $vector_length; $i++) {
7723 + $sum_vector[$i] += $vector[$i];
7724 + }
7725 + }
7726 +
7727 + // Divide each component by the number of vectors to get the average
7728 + $num_vectors = count($vectors);
7729 + for ($i = 0; $i < $vector_length; $i++) {
7730 + $sum_vector[$i] /= $num_vectors;
7731 + }
7732 +
7733 + return $sum_vector;
7734 + }
7735 +
7736 +
7737 +
7738 +
7739 + /**
7740 + * Generates embeddings from input text for MXChat
7741 + */
7742 + public function mxchat_generate_embedding($text) {
7743 + // Enable detailed logging for debugging
7744 + //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
7745 + //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
7746 +
7747 + $options = get_option('mxchat_options');
7748 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
7749 + //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
7750 +
7751 + // Determine provider and endpoint
7752 + if (strpos($selected_model, 'voyage') === 0) {
7753 + $api_key = $options['voyage_api_key'] ?? '';
7754 + $endpoint = 'https://api.voyageai.com/v1/embeddings';
7755 + $provider_name = 'Voyage AI';
7756 + //error_log('[MXCHAT-EMBED] Using Voyage AI API');
7757 + } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
7758 + $api_key = $options['gemini_api_key'] ?? '';
7759 + $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
7760 + $provider_name = 'Google Gemini';
7761 + //error_log('[MXCHAT-EMBED] Using Google Gemini API');
7762 + } else {
7763 + $api_key = $options['api_key'] ?? '';
7764 + $endpoint = 'https://api.openai.com/v1/embeddings';
7765 + $provider_name = 'OpenAI';
7766 + //error_log('[MXCHAT-EMBED] Using OpenAI API');
7767 + }
7768 +
7769 + //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
7770 +
7771 + if (empty($api_key)) {
7772 + $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
7773 + //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
7774 + return $error_message;
7775 + }
7776 +
7777 + // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
7778 + $estimated_tokens = ceil(str_word_count($text) / 0.75);
7779 + //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
7780 +
7781 + if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
7782 + //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
7783 + // Consider truncating text here
7784 + }
7785 +
7786 + // Prepare request body based on provider
7787 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7788 + // Gemini API format
7789 + $request_body = array(
7790 + 'model' => 'models/' . $selected_model,
7791 + 'content' => array(
7792 + 'parts' => array(
7793 + array('text' => $text)
7794 + )
7795 + )
7796 + );
7797 +
7798 + // Set output dimensionality to 1536 for consistency with other models
7799 + $request_body['outputDimensionality'] = 1536;
7800 + } else {
7801 + // OpenAI/Voyage API format
7802 + $request_body = array(
7803 + 'model' => $selected_model,
7804 + 'input' => $text
7805 + );
7806 +
7807 + // Add output_dimension for voyage-3-large model
7808 + if ($selected_model === 'voyage-3-large') {
7809 + $request_body['output_dimension'] = 2048;
7810 + }
7811 + }
7812 +
7813 + //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
7814 +
7815 + // Prepare headers based on provider
7816 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7817 + // Gemini uses API key as query parameter
7818 + $endpoint .= '?key=' . $api_key;
7819 + $headers = array(
7820 + 'Content-Type' => 'application/json'
7821 + );
7822 + } else {
7823 + // OpenAI/Voyage use Bearer token
7824 + $headers = array(
7825 + 'Authorization' => 'Bearer ' . $api_key,
7826 + 'Content-Type' => 'application/json'
7827 + );
7828 + }
7829 +
7830 + // Make API request
7831 + //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
7832 + $response = wp_remote_post($endpoint, array(
7833 + 'body' => wp_json_encode($request_body),
7834 + 'headers' => $headers,
7835 + 'timeout' => 60 // Increased timeout for large inputs
7836 + ));
7837 +
7838 + // Handle wp_remote_post errors
7839 + if (is_wp_error($response)) {
7840 + $error_message = $response->get_error_message();
7841 + //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
7842 + return 'Connection error: ' . $error_message;
7843 + }
7844 +
7845 + // Get and check HTTP response code
7846 + $http_code = wp_remote_retrieve_response_code($response);
7847 + //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
7848 +
7849 + if ($http_code !== 200) {
7850 + $error_body = wp_remote_retrieve_body($response);
7851 + //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
7852 +
7853 + // Try to parse error for more details
7854 + $error_json = json_decode($error_body, true);
7855 + if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
7856 + $error_type = $error_json['error']['type'] ?? 'unknown';
7857 + $error_message = $error_json['error']['message'] ?? 'No message';
7858 + //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
7859 + //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
7860 +
7861 + // Customize error message for common API errors
7862 + if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
7863 + $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
7864 + } elseif ($error_type === 'authentication_error') {
7865 + $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
7866 + }
7867 +
7868 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7869 + return $error_message;
7870 + }
7871 +
7872 + $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
7873 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7874 + return $error_message;
7875 + }
7876 +
7877 + // Parse response body
7878 + $response_body = wp_remote_retrieve_body($response);
7879 + //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
7880 +
7881 + $response_data = json_decode($response_body, true);
7882 +
7883 + if (json_last_error() !== JSON_ERROR_NONE) {
7884 + $error = json_last_error_msg();
7885 + //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
7886 + //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
7887 + return "Failed to parse API response: $error";
7888 + }
7889 +
7890 + // Handle different response formats based on provider
7891 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7892 + // Gemini API response format
7893 + if (isset($response_data['embedding']['values'])) {
7894 + $embedding_dimensions = count($response_data['embedding']['values']);
7895 + //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
7896 +
7897 + // Check if embedding dimensions are as expected (should be 1536)
7898 + if ($embedding_dimensions !== 1536) {
7899 + //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
7900 + }
7901 +
7902 + return $response_data['embedding']['values'];
7903 + } else {
7904 + //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
7905 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7906 +
7907 + if (isset($response_data['error'])) {
7908 + $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
7909 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7910 + return $error_message;
7911 + }
7912 +
7913 + $error_message = "Invalid Gemini API response format: No embedding found";
7914 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7915 + return $error_message;
7916 + }
7917 + } else {
7918 + // OpenAI/Voyage API response format
7919 + if (isset($response_data['data'][0]['embedding'])) {
7920 + $embedding_dimensions = count($response_data['data'][0]['embedding']);
7921 + //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
7922 +
7923 + // Check if embedding dimensions are as expected
7924 + if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
7925 + ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
7926 + //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
7927 + }
7928 +
7929 + return $response_data['data'][0]['embedding'];
7930 + } else {
7931 + //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
7932 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7933 +
7934 + if (isset($response_data['error'])) {
7935 + $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
7936 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7937 + return $error_message;
7938 + }
7939 +
7940 + $error_message = "Invalid API response format: No embedding found";
7941 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7942 + return $error_message;
7943 + }
7944 + }
7945 + }
6014 7946
6015 7947
6016 7948
6017 7949 }