PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.1.6
MxChat – AI Chatbot & Content Generation for WordPress v2.1.6
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 +3332 -1480 2.0.32.1.6 View file →
@@ -37,16 +37,18 @@
37 37 add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 38 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39 39 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
40 40 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'));
41 + add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
42 + add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
42 43 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('admin_post_mxchat_edit_intent', array($this, 'mxchat_handle_edit_intent'));
44 45 add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 46 add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 47 add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 48 add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 49 add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
50 + add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
49 51 add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
50 52 if (isset($this->options['enable_woocommerce_integration']) &&
51 53 ($this->options['enable_woocommerce_integration'] === '1' ||
52 54 $this->options['enable_woocommerce_integration'] === 'on')) {
@@ -55,8 +57,12 @@
55 57 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
56 58 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
57 59 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
58 60 }
61 +
62 + add_action('wp_ajax_mxchat_get_status_updates', array($this, 'ajax_get_status_updates'));
63 + add_action('admin_notices', array($this, 'display_admin_notices'));
64 +
59 65 }
60 66
61 67 // Method to check if the license is active
62 68 private function is_license_active() {
@@ -70,8 +76,11 @@
70 76 'api_key' => '',
71 77 'xai_api_key' => '',
72 78 'claude_api_key' => '',
73 79 'deepseek_api_key' => '',
80 + 'voyage_api_key' => '',
81 + 'gemini_api_key' => '',
82 + 'embedding_model' => 'text-embedding-ada-002',
74 83 '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 84 - Your name is [Chatbot Name].
76 85 - 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 86 - 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.
@@ -81,12 +90,13 @@
81 90 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
82 91 'role_rate_limits' => array(),
83 92 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
84 93 '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'),
94 + '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 95 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
87 96 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
88 - 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
97 + 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
98 + 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
89 99 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
90 100 'append_to_body' => esc_html__('off', 'mxchat'),
91 101 'close_button_color' => esc_html__('#fff', 'mxchat'),
92 102 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
@@ -114,8 +124,10 @@
114 124 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
115 125 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
116 126 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
117 127 'pdf_max_pages' => 69,
128 + 'show_pdf_upload_button' => 'on',
129 + 'show_word_upload_button' => 'on',
118 130
119 131 // Live Agent Integration
120 132 'live_agent_webhook_url' => '',
121 133 'live_agent_secret_key' => '',
@@ -173,26 +185,24 @@
173 185 'mxchat-prompts',
174 186 array($this, 'mxchat_create_prompts_page')
175 187 );
176 188
177 - // Submenu page for Chat Transcripts
178 189 add_submenu_page(
179 - 'mxchat-max', // Corrected parent slug to match the main menu
190 + 'mxchat-max',
180 191 esc_html__('Chat Transcripts', 'mxchat'),
181 192 esc_html__('Transcripts', 'mxchat'),
182 193 'manage_options',
183 194 'mxchat-transcripts',
184 - array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
195 + array($this, 'mxchat_create_transcripts_page')
185 196 );
186 197
187 - // Submenu page for Intents
188 198 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
199 + 'mxchat-max',
200 + esc_html__('MxChat Actions', 'mxchat'),
201 + esc_html__('Actions', 'mxchat'),
202 + 'manage_options',
203 + 'mxchat-actions',
204 + array($this, 'mxchat_actions_page_html')
195 205 );
196 206
197 207 add_submenu_page(
198 208 'mxchat-max',
@@ -224,16 +234,20 @@
224 234 check_ajax_referer('mxchat_save_setting_nonce');
225 235 if (!current_user_can('manage_options')) {
226 236 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
227 237 }
238 +
228 239 $name = isset($_POST['name']) ? $_POST['name'] : '';
229 240 // Strip slashes from the value before saving
230 241 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
242 +
231 243 if (empty($name)) {
232 244 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
233 245 }
246 +
234 247 // Load the full options array
235 248 $options = get_option('mxchat_options', []);
249 +
236 250 // Handle special cases
237 251 switch ($name) {
238 252 case 'additional_popular_questions':
239 253 $questions = json_decode($value, true); // No need for stripslashes here
@@ -242,8 +256,16 @@
242 256 // Also update old option for backwards compatibility
243 257 update_option('additional_popular_questions', $questions);
244 258 }
245 259 break;
260 + case 'email_blocker_header_content':
261 + // Allow HTML content but sanitize it safely
262 + $options[$name] = wp_kses_post($value);
263 + break;
264 + case 'similarity_threshold':
265 + // Save to the options array
266 + $options[$name] = $value;
267 + break;
246 268 case 'user_message_bg_color':
247 269 case 'user_message_font_color':
248 270 case 'bot_message_bg_color':
249 271 case 'bot_message_font_color':
@@ -288,9 +310,11 @@
288 310 if (strpos($name, 'toggle') !== false || in_array($name, [
289 311 'chat_persistence_toggle',
290 312 'privacy_toggle',
291 313 'complianz_toggle',
292 - 'chat_toolbar_toggle' // Removed live_agent_status from here
314 + 'chat_toolbar_toggle',
315 + 'show_pdf_upload_button',
316 + 'show_word_upload_button'
293 317 ])) {
294 318 $options[$name] = ($value === 'on') ? 'on' : 'off';
295 319 } else {
296 320 // Store all other values directly
@@ -297,22 +321,44 @@
297 321 $options[$name] = $value;
298 322 }
299 323 break;
300 324 }
301 - // Save all updates
325 +
326 + // Save all updates to the options array
302 327 $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);
328 +
329 + // Always return success even if WordPress says nothing changed
330 + // (which happens when the value is the same as before)
331 + wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
332 +}
333 +
334 +
335 +/**
336 + * Helper function to compare if a value has changed
337 + * Handles various data types appropriately
338 + */
339 +private function has_value_changed($old_value, $new_value) {
340 + // Handle null values
341 + if ($old_value === null && $new_value === '') {
342 + return false;
309 343 }
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')]);
344 +
345 + // Handle array values (like additional_popular_questions)
346 + if (is_array($old_value) && is_array($new_value)) {
347 + // Convert both to JSON for comparison to handle ordering differences
348 + return json_encode($old_value) !== json_encode($new_value);
314 349 }
350 +
351 + // Handle toggle/checkbox values consistently
352 + if (in_array($old_value, ['on', '1', 1, true]) && in_array($new_value, ['on', '1', 1, true])) {
353 + return false;
354 + }
355 + if (in_array($old_value, ['off', '0', 0, false, '']) && in_array($new_value, ['off', '0', 0, false, ''])) {
356 + return false;
357 + }
358 +
359 + // Default direct comparison
360 + return $old_value !== $new_value;
315 361 }
316 362
317 363 /**
318 364 * Handles AJAX auto-save for prompts and auto-sync settings.
@@ -317,30 +363,41 @@
317 363 /**
318 364 * Handles AJAX auto-save for prompts and auto-sync settings.
319 365 */
320 366 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' );
323 -
324 - if ( ! current_user_can( 'manage_options' ) ) {
325 - wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
367 + check_ajax_referer('mxchat_prompts_setting_nonce', '_ajax_nonce');
368 +
369 + if (!current_user_can('manage_options')) {
370 + wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
326 371 }
327 -
328 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
329 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
330 -
331 - if ( empty( $name ) ) {
332 - wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
372 +
373 + $name = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
374 + $value = isset($_POST['value']) ? sanitize_text_field($_POST['value']) : '';
375 +
376 + if (empty($name)) {
377 + wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
333 378 }
334 -
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' ) ] );
379 +
380 + // Log the values we're trying to save (for debugging)
381 + //error_log('Attempting to save setting: ' . $name . ' = ' . $value);
382 +
383 + // For all auto-sync options
384 + if (strpos($name, 'mxchat_auto_sync_') === 0) {
385 + // Convert 'on'/'off' to '1'/'0' for consistency (if needed)
386 + $option_value = ($value === 'on') ? '1' : '0';
387 +
388 + $result = update_option($name, $option_value);
389 +
390 + if ($result) {
391 + //error_log('Successfully saved setting: ' . $name . ' = ' . $option_value);
392 + wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
393 + } else {
394 + //error_log('Failed to save setting: ' . $name . ' (no changes or error)');
395 + wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
396 + }
397 + return;
341 398 }
342 -
399 +
343 400 // Handle fields stored in the 'mxchat_prompts_options' array.
344 401 // Handle fields stored in the 'mxchat_prompts_options' array.
345 402 if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
346 403 // Extract the key name using regex.
@@ -366,9 +423,8 @@
366 423
367 424
368 425 wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
369 426 }
370 -
371 427 public function mxchat_display_admin_notice() {
372 428 // Success notice
373 429 if ($message = get_transient('mxchat_admin_notice_success')) {
374 430 ?>
@@ -398,188 +454,215 @@
398 454
399 455 public function mxchat_create_admin_page() {
400 456
401 457 ?>
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>
458 + <div class="wrap mxchat-wrapper">
459 + <!-- Hero Section -->
460 + <div class="mxchat-hero">
461 + <h1 class="mxchat-main-title">
462 + <span class="mxchat-gradient-text">MxChat</span> Settings
463 + </h1>
464 + <p class="mxchat-hero-subtitle">
465 + <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
466 + </p>
409 467 </div>
410 - <?php endif; ?>
411 468
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>
469 + <div class="mxchat-content">
470 + <?php if (!$this->is_activated): ?>
471 + <div class="mxchat-pro-card">
472 + <div class="mxchat-pro-notification">
473 + <div class="mxchat-pro-content">
474 + <h3>🚀 Limited Time Offer: Save $20 on MxChat Pro Lifetime Access!</h3>
475 + <p>Unlock <strong>unlimited access</strong> to our growing collection of powerful add-ons including Forms Builder, Theme Customizer, WooCommerce, Perplexity, and more – all included with your lifetime license!</p>
476 + </div>
477 + <div class="mxchat-pro-cta">
478 + <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade to Pro Today', 'mxchat'); ?></a>
479 + <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
480 + </div>
481 + </div>
482 + </div>
483 + <?php endif; ?>
417 484
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>
423 -
424 - <div id="chatbot" class="mxchat-tab-content active">
425 - <div class="mxchat-autosave-section">
426 - <?php do_settings_sections('mxchat-chatbot'); ?>
485 + <!-- Tabs Navigation -->
486 + <div class="mxchat-tabs">
487 + <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
488 + <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></button>
489 + <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
427 490 </div>
428 - </div>
429 491
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>
492 + <!-- Tab Contents -->
493 + <div id="chatbot" class="mxchat-tab-content active">
494 + <div class="mxchat-card">
495 + <div class="mxchat-autosave-section">
496 + <?php do_settings_sections('mxchat-chatbot'); ?>
497 + </div>
437 498 </div>
438 - <div class="section-divider"></div>
499 + </div>
439 500
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>
501 + <div id="embed" class="mxchat-tab-content">
502 + <div class="mxchat-card">
503 + <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
504 + <div class="mxchat-autosave-section">
505 + <table class="form-table">
506 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
507 + </table>
508 + </div>
445 509 </div>
446 510
447 - <div class="section-divider"></div>
448 -
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>
511 + <div class="mxchat-card">
512 + <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
513 + <div class="mxchat-autosave-section">
514 + <table class="form-table">
515 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
516 + </table>
517 + </div>
454 518 </div>
455 519
456 - <div class="section-divider"></div>
457 -
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>
520 + <div class="mxchat-card">
521 + <h2><?php esc_html_e('Toolbar Settings & Intents', 'mxchat'); ?></h2>
522 + <div class="mxchat-autosave-section">
523 + <table class="form-table">
524 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
525 + </table>
526 + </div>
463 527 </div>
464 528
465 - <div class="section-divider"></div>
466 -
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>
529 + <div class="mxchat-card">
530 + <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
531 + <div class="mxchat-autosave-section">
532 + <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>
533 + <table class="form-table">
534 + <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
535 + </table>
536 + </div>
474 537 </div>
475 538 </div>
476 - </div>
477 539
540 + <div id="general" class="mxchat-tab-content">
541 + <div class="mxchat-card">
542 + <?php do_settings_sections('mxchat-general'); ?>
543 + <div class="video-tutorials-section">
478 544
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 -
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>
545 + <div class="tutorial-grid">
546 + <div class="tutorial-item">
547 + <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
548 + <div class="video-description">
549 + <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
550 + <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
551 + <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>
552 + <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
553 + </a>
501 554 </div>
555 + </div>
502 556
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>
557 + <div class="tutorial-item">
558 + <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
559 + <div class="video-description">
560 + <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>
561 + <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
562 + <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>
563 + <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
564 + </a>
511 565 </div>
566 + </div>
512 567
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>
568 + <div class="tutorial-item">
569 + <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
570 + <div class="video-description">
571 + <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>
572 + <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
573 + <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>
574 + <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
575 + </a>
521 576 </div>
577 + </div>
522 578
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>
579 + <div class="tutorial-item">
580 + <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
581 + <div class="video-description">
582 + <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>
583 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
584 + <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
585 + <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>
586 + <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
587 + </a>
528 588 </div>
589 + </div>
529 590
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>
591 + <div class="tutorial-item">
592 + <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
593 + <div class="video-description">
594 + <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>
595 + <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
596 + <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>
597 + <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
598 + </a>
535 599 </div>
536 -
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>
600 + </div>
601 +
602 + <div class="tutorial-item">
603 + <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
604 + <div class="video-description">
605 + <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>
606 + <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
607 + <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>
608 + <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
609 + </a>
542 610 </div>
611 + </div>
543 612
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>
613 + <div class="tutorial-item">
614 + <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
615 + <div class="video-description">
616 + <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>
617 + <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
618 + <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>
619 + <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
620 + </a>
549 621 </div>
622 + </div>
550 623
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>
624 + <div class="tutorial-item">
625 + <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
626 + <div class="video-description">
627 + <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>
628 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
629 + <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
630 + <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>
631 + <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
632 + </a>
556 633 </div>
634 + </div>
557 635
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>
636 + <div class="tutorial-item">
637 + <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
638 + <div class="video-description">
639 + <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>
640 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
641 + <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
642 + <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>
643 + <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
644 + </a>
563 645 </div>
646 + </div>
647 + </div>
564 648
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>
649 + <div class="support-section">
650 + <h3><?php echo esc_html__('Need Help?', 'mxchat'); ?></h3>
651 + <div class="support-content">
652 + <p>
653 + <?php echo esc_html__('If you\'re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
654 + <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
655 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
656 + </p>
657 + <p>
658 + <?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>.
659 + </p>
660 + </div>
661 + </div>
662 + </div>
663 + </div>
573 664 </div>
574 -
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>
580 - </div>
581 -
582 665 </div>
583 666 </div>
584 667 <?php
585 668 }
@@ -584,73 +667,348 @@
584 667 <?php
585 668 }
586 669
587 670
588 -
589 671 public function mxchat_create_transcripts_page() {
590 672 global $wpdb;
591 673 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
592 -
674 +
593 675 // Get basic stats
594 676 $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
595 677 $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 != ''");
678 +
679 + // Count unique users with detailed breakdown
680 + $total_users = $wpdb->get_var("
681 + SELECT COUNT(DISTINCT
682 + CASE
683 + WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
684 + WHEN user_id != 0 THEN CONCAT('user_', user_id)
685 + WHEN user_identifier NOT LIKE 'Tech-Savvy User'
686 + AND user_identifier NOT LIKE 'Detail-Oriented User'
687 + AND user_identifier NOT LIKE 'Language Learner'
688 + AND user_identifier NOT LIKE 'Casual Browser'
689 + AND user_identifier NOT LIKE 'Policy Enforcer'
690 + AND user_identifier NOT LIKE 'Researcher'
691 + AND user_identifier NOT LIKE 'Loyalty Member'
692 + AND user_identifier NOT LIKE 'Gift Buyer'
693 + AND user_identifier NOT LIKE 'Parent or Caregiver'
694 + THEN user_identifier
695 + ELSE session_id
696 + END
697 + )
698 + FROM $table_name
699 + WHERE role != 'assistant'
700 + ");
701 +
702 + // Get user type breakdown
703 + $registered_users = $wpdb->get_var("
704 + SELECT COUNT(DISTINCT user_email)
705 + FROM $table_name
706 + WHERE user_email != '' AND user_email IS NOT NULL
707 + ");
708 +
709 + $guest_users = $wpdb->get_var("
710 + SELECT COUNT(DISTINCT user_identifier)
711 + FROM $table_name
712 + WHERE (user_email = '' OR user_email IS NULL)
713 + AND role != 'assistant'
714 + AND user_identifier NOT LIKE 'Tech-Savvy User'
715 + AND user_identifier NOT LIKE 'Detail-Oriented User'
716 + AND user_identifier NOT LIKE 'Language Learner'
717 + AND user_identifier NOT LIKE 'Casual Browser'
718 + AND user_identifier NOT LIKE 'Policy Enforcer'
719 + AND user_identifier NOT LIKE 'Researcher'
720 + AND user_identifier NOT LIKE 'Loyalty Member'
721 + AND user_identifier NOT LIKE 'Gift Buyer'
722 + AND user_identifier NOT LIKE 'Parent or Caregiver'
723 + ");
724 +
725 + // Get agent test messages count
726 + $agent_tests = $wpdb->get_var("
727 + SELECT COUNT(DISTINCT session_id)
728 + FROM $table_name
729 + WHERE user_identifier IN (
730 + 'Tech-Savvy User',
731 + 'Detail-Oriented User',
732 + 'Language Learner',
733 + 'Casual Browser',
734 + 'Policy Enforcer',
735 + 'Researcher',
736 + 'Loyalty Member',
737 + 'Gift Buyer',
738 + 'Parent or Caregiver'
739 + )
740 + ");
597 741 ?>
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>
742 + <div class="wrap mxchat-transcripts-wrapper">
743 + <!-- Hero Section -->
744 + <div class="mxchat-transcripts-hero">
745 + <h1 class="mxchat-main-title">
746 + Chat <span class="mxchat-gradient-text">Transcripts</span>
747 + </h1>
748 + <p class="mxchat-hero-subtitle">
749 + <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
750 + </p>
751 + </div>
752 + <div class="mxchat-content">
753 + <!-- Stats Cards -->
754 + <div class="mxchat-stats-grid">
755 + <div class="mxchat-stat-card">
756 + <div class="stat-icon">💬</div>
757 + <div class="stat-content">
758 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
759 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
760 + </div>
606 761 </div>
607 - </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>
762 + <div class="mxchat-stat-card">
763 + <div class="stat-icon">📝</div>
764 + <div class="stat-content">
765 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
766 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
767 + </div>
613 768 </div>
769 + <div class="mxchat-stat-card">
770 + <div class="stat-icon">👥</div>
771 + <div class="stat-content">
772 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
773 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
774 + <span class="stat-sublabel">
775 + <?php
776 + echo sprintf(
777 + esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
778 + $registered_users,
779 + $guest_users,
780 + $agent_tests
781 + );
782 + ?>
783 + </span>
784 + </div>
785 + </div>
614 786 </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>
787 + <!-- Search and Filter Controls -->
788 + <div class="mxchat-controls-wrapper">
789 + <div class="mxchat-search-box">
790 + <input type="text" id="mxchat-search-transcripts"
791 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
792 + class="regular-text">
620 793 </div>
794 + <form id="mxchat-delete-form" method="post">
795 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
796 + <div class="mxchat-controls">
797 + <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
798 + <span class="dashicons dashicons-download"></span>
799 + <?php esc_html_e('Export All Chats', 'mxchat'); ?>
800 + </button>
801 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
802 + <span class="dashicons dashicons-yes-alt"></span>
803 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
804 + </button>
805 + <button type="submit" class="button delete-chats-button">
806 + <span class="dashicons dashicons-trash"></span>
807 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
808 + </button>
809 + </div>
810 + </form>
621 811 </div>
812 + <!-- Transcripts Container -->
813 + <div id="mxchat-transcripts"></div>
622 814 </div>
815 + </div>
816 + <?php
817 +}
623 818
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">
630 - </div>
819 +public function export_chat_transcripts() {
820 + if (!current_user_can('manage_options')) {
821 + wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
822 + }
631 823
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>
643 - </div>
644 - </form>
645 - </div>
824 + check_ajax_referer('mxchat_export_transcripts', 'security');
646 825
647 - <!-- Transcripts Container -->
648 - <div id="mxchat-transcripts"></div>
649 - </div>
650 - <?php
826 + global $wpdb;
827 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
828 +
829 + // Get all transcripts ordered by session and timestamp
830 + $results = $wpdb->get_results(
831 + "SELECT session_id, user_email, user_identifier, role, message, timestamp
832 + FROM {$table_name}
833 + ORDER BY session_id, timestamp ASC"
834 + );
835 +
836 + if (empty($results)) {
837 + wp_send_json_error(array('message' => 'No transcripts found.'));
838 + wp_die();
839 + }
840 +
841 + // Set headers for CSV download
842 + header('Content-Type: text/csv');
843 + header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
844 + header('Pragma: no-cache');
845 + header('Expires: 0');
846 +
847 + // Create output stream
848 + $output = fopen('php://output', 'w');
849 +
850 + // Add UTF-8 BOM for proper Excel encoding
851 + fputs($output, "\xEF\xBB\xBF");
852 +
853 + // Add CSV headers
854 + fputcsv($output, array(
855 + 'Session ID',
856 + 'Email',
857 + 'User Identifier',
858 + 'Role',
859 + 'Message',
860 + 'Timestamp'
861 + ));
862 +
863 + // Add data rows
864 + foreach ($results as $row) {
865 + fputcsv($output, array(
866 + $row->session_id,
867 + $row->user_email,
868 + $row->user_identifier,
869 + $row->role,
870 + $row->message,
871 + $row->timestamp
872 + ));
873 + }
874 +
875 + fclose($output);
876 + wp_die();
651 877 }
652 878
879 +public function mxchat_fetch_chat_history() {
880 + global $wpdb;
881 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
882 +
883 + if (!current_user_can('manage_options')) {
884 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
885 + }
886 +
887 + // First get unique session IDs ordered by most recent message in each session
888 + $session_ids = $wpdb->get_col(
889 + $wpdb->prepare(
890 + "SELECT DISTINCT session_id
891 + FROM {$table_name}
892 + GROUP BY session_id
893 + ORDER BY MAX(timestamp) DESC"
894 + )
895 + );
896 +
897 + if (empty($session_ids)) {
898 + wp_die(esc_html__('No chat history available.', 'mxchat'));
899 + }
900 +
901 + ob_start();
902 + echo '<div class="mxchat-transcript">';
903 +
904 + // Iterate through sessions from newest to oldest
905 + foreach ($session_ids as $session_id) {
906 + // Get the email associated with this session (if available)
907 + $email = $wpdb->get_var(
908 + $wpdb->prepare(
909 + "SELECT user_email
910 + FROM {$table_name}
911 + WHERE session_id = %s AND user_email != ''
912 + ORDER BY timestamp ASC
913 + LIMIT 1",
914 + $session_id
915 + )
916 + );
917 +
918 + // Debug: Check email retrieval
919 + if (empty($email)) {
920 + //error_log("No email found for session {$session_id}");
921 + } else {
922 + //error_log("Email for session {$session_id}: " . $email);
923 + }
924 +
925 + // Get messages for this session ordered by timestamp
926 + $messages = $wpdb->get_results(
927 + $wpdb->prepare(
928 + "SELECT * FROM {$table_name}
929 + WHERE session_id = %s
930 + ORDER BY timestamp ASC",
931 + $session_id
932 + )
933 + );
934 +
935 + // Start session block
936 + echo '<div class="mxchat-session">';
937 + echo '<div class="mxchat-session-header">';
938 + // Wrap checkbox and session ID in one block
939 + echo '<div class="mxchat-session-id">';
940 + echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
941 + echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
942 + echo '</div>';
943 +
944 + // Place email directly below the session ID block
945 + if (!empty($email)) {
946 + echo '<div class="mxchat-session-email">';
947 + echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
948 + echo '</div>';
949 + }
950 + echo '</div>';
951 +
952 + echo '<div class="mxchat-messages">';
953 +
954 + // Display messages for this session
955 + foreach ($messages as $transcript) {
956 + $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
957 +
958 + // Determine message styling
959 + switch ($transcript->role) {
960 + case 'assistant':
961 + case 'bot':
962 + $message_class = 'bot-message';
963 + $display_role = esc_html__('Chatbot', 'mxchat');
964 + break;
965 + case 'user':
966 + $message_class = 'user-message';
967 + $display_role = !empty($transcript->user_identifier)
968 + ? sanitize_text_field($transcript->user_identifier)
969 + : esc_html__('User', 'mxchat');
970 + break;
971 + case 'agent':
972 + $message_class = 'agent-message';
973 + $display_role = esc_html__('Agent', 'mxchat');
974 + break;
975 + default:
976 + $message_class = 'unknown-message';
977 + $display_role = esc_html__('Unknown', 'mxchat');
978 + }
979 +
980 + // Process message content
981 + $message_content = wp_kses(
982 + stripslashes($transcript->message),
983 + [
984 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
985 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
986 + 'a' => ['href' => [], 'title' => []]
987 + ]
988 + );
989 + $message_content = nl2br($message_content);
990 +
991 + // Render message
992 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
993 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
994 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
995 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
996 + echo '</div>';
997 + }
998 +
999 + // Close session block
1000 + echo '</div></div>';
1001 + }
1002 +
1003 + echo '</div>';
1004 + $output = ob_get_clean();
1005 + echo $output;
1006 + wp_die();
1007 +}
1008 +
1009 +
1010 +
653 1011 public function mxchat_create_prompts_page() {
654 1012 global $wpdb;
655 1013 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
656 1014
@@ -731,10 +1089,39 @@
731 1089 <div class="mxchat-content">
732 1090 <!-- Import Settings Card -->
733 1091 <div class="mxchat-card">
734 1092 <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>
1093 +<?php
1094 +// Check if the appropriate embedding API key exists
1095 +$embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
1096 +$has_openai_key = !empty($this->options['api_key']);
1097 +$has_voyage_key = !empty($this->options['voyage_api_key']);
736 1098
1099 +// Determine if they have the needed API key for their selected embedding model
1100 +$has_required_key = false;
1101 +$required_key_type = '';
1102 +
1103 +if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
1104 + $has_required_key = true;
1105 + $required_key_type = 'OpenAI';
1106 +} elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
1107 + $has_required_key = true;
1108 + $required_key_type = 'Voyage AI';
1109 +} elseif (strpos($embedding_model, 'text-embedding-') !== false) {
1110 + $required_key_type = 'OpenAI';
1111 +} elseif (strpos($embedding_model, 'voyage-') !== false) {
1112 + $required_key_type = 'Voyage AI';
1113 +}
1114 +?>
1115 +
1116 +<div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
1117 + <?php if ($has_required_key): ?>
1118 + <p><span class="dashicons dashicons-yes-alt"></span> <?php echo wp_kses_post(sprintf(__('We detected your %s API key. <strong>You must have added credits to your %s account</strong> before using the knowledgebase.', 'mxchat'), $required_key_type, $required_key_type)); ?></p>
1119 + <?php else: ?>
1120 + <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>
1121 + <?php endif; ?>
1122 +</div>
1123 +
737 1124 <!-- Tab Contents -->
738 1125 <div class="mxchat-tab-contents">
739 1126 <!-- Default Database Tab -->
740 1127 <div id="default-db" class="mxchat-tab-content active">
@@ -759,8 +1146,9 @@
759 1146 <span class="mxchat-toggle-label">
760 1147 <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
761 1148 </span>
762 1149 </div>
1150 +
763 1151 <div class="mxchat-toggle-container">
764 1152 <label class="mxchat-toggle-switch">
765 1153 <input type="checkbox"
766 1154 name="mxchat_auto_sync_pages"
@@ -773,8 +1161,64 @@
773 1161 <span class="mxchat-toggle-label">
774 1162 <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
775 1163 </span>
776 1164 </div>
1165 +
1166 +
1167 +
1168 +
1169 + <!-- Replace the existing custom post types section with this code -->
1170 +<div class="mxchat-section-content">
1171 + <div class="mxchat-custom-post-types-header">
1172 + <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
1173 + <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
1174 + <span class="mxchat-toggle-icon">▼</span>
1175 + </button>
1176 + </div>
1177 +
1178 + <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
1179 + <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
1180 + <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
1181 +
1182 + <div class="mxchat-custom-post-types">
1183 + <?php
1184 + $post_types = $this->get_public_post_types();
1185 +
1186 + // Skip post and page as they're handled separately
1187 + unset($post_types['post']);
1188 + unset($post_types['page']);
1189 +
1190 + if (!empty($post_types)) {
1191 + foreach ($post_types as $post_type => $label) {
1192 + $option_name = 'mxchat_auto_sync_' . $post_type;
1193 + $is_enabled = get_option($option_name, '0');
1194 + ?>
1195 + <div class="mxchat-toggle-container">
1196 + <label class="mxchat-toggle-switch">
1197 + <input type="checkbox"
1198 + name="<?php echo esc_attr($option_name); ?>"
1199 + class="mxchat-autosave-field"
1200 + value="1"
1201 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1202 + <?php checked($is_enabled, '1'); ?>>
1203 + <span class="mxchat-toggle-slider"></span>
1204 + </label>
1205 + <span class="mxchat-toggle-label">
1206 + <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
1207 + </span>
1208 + </div>
1209 + <?php
1210 + }
1211 + } else {
1212 + echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
1213 + }
1214 + ?>
1215 + </div>
1216 + </div>
1217 +</div>
1218 +
1219 +
1220 +
777 1221 </div>
778 1222 </div>
779 1223 </div>
780 1224
@@ -781,10 +1225,9 @@
781 1225 <!-- Import Methods -->
782 1226 <div class="mxchat-import-methods">
783 1227 <div class="mxchat-method-card">
784 1228 <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>
1229 + <p><span class="red-warning">IMPORTANT:</span> <?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p> </div>
787 1230 <div class="mxchat-method-card">
788 1231 <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
789 1232 <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
790 1233 </div>
@@ -820,85 +1263,177 @@
820 1263 </div>
821 1264 <?php endif; ?>
822 1265
823 1266 <!-- 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; ?>
1267 +<?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
1268 + <div class="mxchat-status-card">
1269 + <div class="mxchat-status-header">
1270 + <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1271 + <?php if ($is_processing) : ?>
1272 + <form method="post" class="mxchat-stop-form"
1273 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1274 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1275 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1276 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1277 + </button>
1278 + </form>
1279 + <?php endif; ?>
1280 +
1281 + <?php if ($pdf_status['status'] === 'error') : ?>
1282 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Error', 'mxchat'); ?></span>
1283 + <?php endif; ?>
1284 + </div>
1285 + <div class="mxchat-progress-bar">
1286 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1287 + </div>
1288 + <div class="mxchat-status-details">
1289 + <p><?php printf(
1290 + esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1291 + absint($pdf_status['processed_pages']),
1292 + absint($pdf_status['total_pages']),
1293 + absint($pdf_status['percentage'])
1294 + ); ?></p>
1295 + <p><?php printf(
1296 + esc_html__('Status: %s', 'mxchat'),
1297 + esc_html(ucfirst($pdf_status['status']))
1298 + ); ?></p>
1299 + <p><?php printf(
1300 + esc_html__('Last update: %s', 'mxchat'),
1301 + esc_html($pdf_status['last_update'])
1302 + ); ?></p>
1303 +
1304 + <?php if (!empty($pdf_status['error'])) : ?>
1305 + <div class="mxchat-error-notice">
1306 + <p class="error"><?php echo esc_html($pdf_status['error']); ?></p>
1307 + </div>
1308 + <?php endif; ?>
1309 + </div>
1310 + </div>
1311 +<?php endif; ?>
859 1312
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; ?>
1313 +
1314 +<!-- Single URL Submission Status -->
1315 +<?php
1316 +// Get single URL status
1317 +$single_url_status = $this->get_single_url_status();
1318 +$is_active_processing =
1319 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
1320 + ($pdf_status && $pdf_status['status'] === 'processing');
1321 +?>
1322 +
1323 +<div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
1324 + <?php if ($single_url_status && !$is_active_processing) : ?>
1325 + <div class="mxchat-status-card">
1326 + <div class="mxchat-status-header">
1327 + <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
1328 + <?php if ($single_url_status['status'] === 'failed') : ?>
1329 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
1330 + <?php else : ?>
1331 + <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
1332 + <?php endif; ?>
1333 + </div>
1334 + <div class="mxchat-status-details">
1335 + <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
1336 + <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
1337 + <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
1338 + </a>
1339 + </p>
1340 + <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
1341 +
1342 + <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
1343 + <div class="mxchat-error-notice">
1344 + <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
897 1345 </div>
1346 + <?php endif; ?>
898 1347
1348 + <?php if ($single_url_status['status'] === 'complete') : ?>
1349 + <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>
1350 + <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
1351 + <?php endif; ?>
1352 + </div>
1353 + </div>
1354 + <?php endif; ?>
1355 +</div>
899 1356
1357 +
1358 +<?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
1359 + <div class="mxchat-status-card">
1360 + <div class="mxchat-status-header">
1361 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
1362 + <?php if ($is_processing) : ?>
1363 + <form method="post" class="mxchat-stop-form"
1364 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1365 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1366 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1367 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1368 + </button>
1369 + </form>
1370 + <?php endif; ?>
1371 + </div>
1372 + <div class="mxchat-progress-bar">
1373 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
1374 + </div>
1375 + <div class="mxchat-status-details">
1376 + <p><?php printf(
1377 + esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
1378 + absint($sitemap_status['processed_urls']),
1379 + absint($sitemap_status['total_urls']),
1380 + absint($sitemap_status['percentage'])
1381 + ); ?></p>
1382 + <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
1383 + <div class="mxchat-error-notice">
1384 + <?php if (!empty($sitemap_status['error'])) : ?>
1385 + <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
1386 + <?php endif; ?>
1387 + <?php if (!empty($sitemap_status['last_error'])) : ?>
1388 + <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
1389 + <?php endif; ?>
900 1390 </div>
1391 + <?php endif; ?>
1392 +
1393 + <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
1394 + <div class="mxchat-failed-urls">
1395 + <h5><?php echo sprintf(esc_html__('Failed URLs (%d)', 'mxchat'), absint($sitemap_status['failed_urls'])); ?></h5>
1396 + <?php if (!empty($sitemap_status['failed_urls_list'])) : ?>
1397 + <div class="mxchat-failed-urls-list" style="max-height: 200px; overflow-y: auto; margin-top: 10px;">
1398 + <table class="widefat striped">
1399 + <thead>
1400 + <tr>
1401 + <th><?php esc_html_e('URL', 'mxchat'); ?></th>
1402 + <th><?php esc_html_e('Error', 'mxchat'); ?></th>
1403 + <th><?php esc_html_e('Time', 'mxchat'); ?></th>
1404 + </tr>
1405 + </thead>
1406 + <tbody>
1407 + <?php foreach ($sitemap_status['failed_urls_list'] as $failed_url) : ?>
1408 + <tr>
1409 + <td style="word-break: break-all;">
1410 + <a href="<?php echo esc_url($failed_url['url']); ?>" target="_blank" rel="noopener noreferrer">
1411 + <?php echo esc_html(strlen($failed_url['url']) > 60 ? substr($failed_url['url'], 0, 57) . '...' : $failed_url['url']); ?>
1412 + </a>
1413 + </td>
1414 + <td><?php echo esc_html($failed_url['error']); ?></td>
1415 + <td><?php echo esc_html(human_time_diff($failed_url['time'], time()) . ' ' . __('ago', 'mxchat')); ?></td>
1416 + </tr>
1417 + <?php endforeach; ?>
1418 + </tbody>
1419 + </table>
1420 + </div>
1421 + <?php endif; ?>
1422 + </div>
1423 + <?php endif; ?>
1424 + </div>
1425 + </div>
1426 +<?php endif; ?>
1427 +
1428 +
1429 + </div>
1430 +
1431 +
1432 + </div>
1433 +
1434 +
1435 +
901 1436 </div>
902 1437
903 1438 <!-- Direct Content Submission Card -->
904 1439 <div class="mxchat-card">
@@ -980,16 +1515,19 @@
980 1515 <?php echo esc_textarea($prompt->article_content); ?>
981 1516 </textarea>
982 1517 </td>
983 1518 <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; ?>
1519 + <div class="url-view">
1520 + <?php if (!empty($prompt->source_url)) : ?>
1521 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
1522 + <span class="dashicons dashicons-external"></span>
1523 + <?php esc_html_e('View Source', 'mxchat'); ?>
1524 + </a>
1525 + <?php else : ?>
1526 + <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
1527 + <?php endif; ?>
1528 + </div>
1529 + <input type="text" class="url-edit" style="display:none;" value="<?php echo esc_attr($prompt->source_url); ?>" />
992 1530 </td>
993 1531 <td class="mxchat-actions-cell">
994 1532 <button class="mxchat-button-icon edit-button"
995 1533 data-id="<?php echo esc_attr($prompt->id); ?>">
@@ -1091,30 +1629,154 @@
1091 1629
1092 1630 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1093 1631 exit;
1094 1632 }
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 1633
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 1634
1110 - if (is_wp_error($response)) {
1111 - return null;
1635 +public function mxchat_generate_embedding($text) {
1636 + // Enable detailed logging for debugging
1637 + //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
1638 + //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
1639 +
1640 + $options = get_option('mxchat_options');
1641 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
1642 + //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
1643 +
1644 + // Determine provider and endpoint
1645 + if (strpos($selected_model, 'voyage') === 0) {
1646 + $api_key = $options['voyage_api_key'] ?? '';
1647 + $endpoint = 'https://api.voyageai.com/v1/embeddings';
1648 + $provider_name = 'Voyage AI';
1649 + //error_log('[MXCHAT-EMBED] Using Voyage AI API');
1650 + } else {
1651 + $api_key = $options['api_key'] ?? '';
1652 + $endpoint = 'https://api.openai.com/v1/embeddings';
1653 + $provider_name = 'OpenAI';
1654 + //error_log('[MXCHAT-EMBED] Using OpenAI API');
1655 + }
1656 +
1657 + //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
1658 +
1659 + if (empty($api_key)) {
1660 + $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
1661 + //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
1662 + return $error_message;
1663 + }
1664 +
1665 + // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
1666 + $estimated_tokens = ceil(str_word_count($text) / 0.75);
1667 + //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
1668 +
1669 + if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false) {
1670 + //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
1671 + // Consider truncating text here
1672 + }
1673 +
1674 + // Prepare request body
1675 + $request_body = array(
1676 + 'model' => $selected_model,
1677 + 'input' => $text
1678 + );
1679 +
1680 + // Add output_dimension for voyage-3-large model
1681 + if ($selected_model === 'voyage-3-large') {
1682 + $request_body['output_dimension'] = 2048;
1683 + }
1684 +
1685 + //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
1686 +
1687 + // Make API request
1688 + //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
1689 + $response = wp_remote_post($endpoint, array(
1690 + 'body' => wp_json_encode($request_body),
1691 + 'headers' => array(
1692 + 'Authorization' => 'Bearer ' . $api_key,
1693 + 'Content-Type' => 'application/json'
1694 + ),
1695 + 'timeout' => 60 // Increased timeout for large inputs
1696 + ));
1697 +
1698 + // Handle wp_remote_post errors
1699 + if (is_wp_error($response)) {
1700 + $error_message = $response->get_error_message();
1701 + //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
1702 + return 'Connection error: ' . $error_message;
1703 + }
1704 +
1705 + // Get and check HTTP response code
1706 + $http_code = wp_remote_retrieve_response_code($response);
1707 + //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
1708 +
1709 + if ($http_code !== 200) {
1710 + $error_body = wp_remote_retrieve_body($response);
1711 + //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
1712 +
1713 + // Try to parse error for more details
1714 + $error_json = json_decode($error_body, true);
1715 + if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
1716 + $error_type = $error_json['error']['type'] ?? 'unknown';
1717 + $error_message = $error_json['error']['message'] ?? 'No message';
1718 + //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
1719 + //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
1720 +
1721 + // Customize error message for common API errors
1722 + if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
1723 + $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
1724 + } elseif ($error_type === 'authentication_error') {
1725 + $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
1726 + }
1727 +
1728 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
1729 + return $error_message;
1112 1730 }
1731 +
1732 + $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
1733 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
1734 + return $error_message;
1735 + }
1736 +
1737 + // Parse response body
1738 + $response_body = wp_remote_retrieve_body($response);
1739 + //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
1740 +
1741 + $response_data = json_decode($response_body, true);
1742 +
1743 + if (json_last_error() !== JSON_ERROR_NONE) {
1744 + $error = json_last_error_msg();
1745 + //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
1746 + //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
1747 + return "Failed to parse API response: $error";
1748 + }
1749 +
1750 + // Both APIs use the same structure, so we can extract the embedding the same way
1751 + if (isset($response_data['data'][0]['embedding'])) {
1752 + $embedding_dimensions = count($response_data['data'][0]['embedding']);
1753 + //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
1754 +
1755 + // Check if embedding dimensions are as expected
1756 + if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
1757 + ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
1758 + //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
1759 + }
1760 +
1761 + return $response_data['data'][0]['embedding'];
1762 + } else {
1763 + //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
1764 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
1765 +
1766 + if (isset($response_data['error'])) {
1767 + $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
1768 + //error_log('[MXCHAT-EMBED] ' . $error_message);
1769 + return $error_message;
1770 + }
1771 +
1772 + $error_message = "Invalid API response format: No embedding found";
1773 + //error_log('[MXCHAT-EMBED] ' . $error_message);
1774 + return $error_message;
1775 + }
1776 +}
1113 1777
1114 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1115 - return $response_data['data'][0]['embedding'] ?? null;
1116 - }
1778 +
1117 1779 public function mxchat_delete_chat_history() {
1118 1780 if (!current_user_can('manage_options')) {
1119 1781 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1120 1782 wp_die();
@@ -1202,45 +1864,106 @@
1202 1864 }
1203 1865
1204 1866
1205 1867
1206 -// Add this method to handle post updates
1868 +/**
1869 + * Get all public post types
1870 + *
1871 + * @return array Associative array of post type names and labels
1872 + */
1873 +private function get_public_post_types() {
1874 + $post_types = get_post_types(array('public' => true), 'objects');
1875 + $post_type_options = array();
1876 +
1877 + foreach ($post_types as $post_type) {
1878 + $post_type_options[$post_type->name] = $post_type->label;
1879 + }
1880 +
1881 + return $post_type_options;
1882 +}
1883 +
1884 +/**
1885 + * Handle post updates and process content for the chatbot
1886 + *
1887 + * @param int $post_id The ID of the post being saved
1888 + * @param WP_Post $post The post object
1889 + * @param bool $update Whether this is an existing post being updated
1890 + */
1207 1891 public function handle_post_update($post_id, $post, $update) {
1208 1892 // Basic validation checks
1209 1893 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1210 1894 return;
1211 1895 }
1212 -
1896 +
1213 1897 // Only process published content
1214 - if (!in_array($post->post_status, array('publish'))) {
1898 + if ($post->post_status !== 'publish') {
1215 1899 return;
1216 1900 }
1217 -
1901 +
1902 + $post_type = $post->post_type;
1903 +
1218 1904 // 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;
1905 + $should_sync = false;
1906 +
1907 + // Check built-in post types first
1908 + if ($post_type === 'post' && get_option('mxchat_auto_sync_posts') === '1') {
1909 + $should_sync = true;
1910 + } else if ($post_type === 'page' && get_option('mxchat_auto_sync_pages') === '1') {
1911 + $should_sync = true;
1912 + } else {
1913 + // Check custom post types
1914 + $option_name = 'mxchat_auto_sync_' . $post_type;
1915 + if (get_option($option_name) === '1') {
1916 + $should_sync = true;
1917 + }
1222 1918 }
1223 -
1224 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1225 - if (get_option($sync_option) != '1') {
1919 +
1920 + if (!$should_sync) {
1226 1921 return;
1227 1922 }
1228 -
1923 +
1229 1924 // Prepare content and URL
1230 1925 $content = wp_strip_all_tags($post->post_content);
1926 +
1927 + // For custom post types like job_listing, include additional fields
1928 + if ($post_type === 'job_listing') {
1929 + // Add title as it's important for job listings
1930 + $content = $post->post_title . "\n\n" . $content;
1931 +
1932 + // Add job-specific meta if available
1933 + $job_location = get_post_meta($post_id, '_job_location', true);
1934 + if (!empty($job_location)) {
1935 + $content .= "\n\nLocation: " . $job_location;
1936 + }
1937 +
1938 + // Get job type terms
1939 + $job_types = get_the_terms($post_id, 'job_listing_type');
1940 + if (!empty($job_types) && !is_wp_error($job_types)) {
1941 + $types = array();
1942 + foreach ($job_types as $type) {
1943 + $types[] = $type->name;
1944 + }
1945 + $content .= "\n\nJob Type: " . implode(', ', $types);
1946 + }
1947 +
1948 + // Get company name if available
1949 + $company_name = get_post_meta($post_id, '_company_name', true);
1950 + if (!empty($company_name)) {
1951 + $content .= "\n\nCompany: " . $company_name;
1952 + }
1953 + }
1954 +
1231 1955 $url = get_permalink($post_id);
1232 -
1956 +
1233 1957 // Generate embedding vector
1234 1958 $embedding_vector = $this->mxchat_generate_embedding($content);
1235 1959 if (!$embedding_vector) {
1236 1960 return;
1237 1961 }
1238 -
1962 +
1239 1963 // Check for Pinecone addon and its settings
1240 1964 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1241 1965 $use_pinecone = false;
1242 -
1243 1966 if ($pinecone_settings && is_array($pinecone_settings)) {
1244 1967 // Check if Pinecone is enabled and all required settings are present
1245 1968 $use_pinecone = (
1246 1969 isset($pinecone_settings['mxchat_use_pinecone']) &&
@@ -1250,9 +1973,9 @@
1250 1973 !empty($pinecone_settings['mxchat_pinecone_index']) &&
1251 1974 !empty($pinecone_settings['mxchat_pinecone_environment'])
1252 1975 );
1253 1976 }
1254 -
1977 +
1255 1978 if ($use_pinecone) {
1256 1979 // Use Pinecone
1257 1980 $pinecone_result = $this->store_in_pinecone_main(
1258 1981 $embedding_vector,
@@ -1261,11 +1984,10 @@
1261 1984 $pinecone_settings['mxchat_pinecone_api_key'],
1262 1985 $pinecone_settings['mxchat_pinecone_environment'],
1263 1986 $pinecone_settings['mxchat_pinecone_index']
1264 1987 );
1265 -
1266 1988 if (!$pinecone_result['success']) {
1267 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1989 + // Fallback to WordPress DB
1268 1990 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1269 1991 }
1270 1992 } else {
1271 1993 // Fallback to WordPress DB storage
@@ -1270,9 +1992,122 @@
1270 1992 } else {
1271 1993 // Fallback to WordPress DB storage
1272 1994 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1273 1995 }
1996 +
1274 1997 }
1998 +/**
1999 + * Handle deletion of posts from both Pinecone and WordPress DB
2000 + *
2001 + * @param int $post_id The ID of the post being deleted
2002 + * @return void
2003 + */
2004 +public function mxchat_handle_post_delete($post_id) {
2005 + // Get post data before it's deleted
2006 + $post = get_post($post_id);
2007 +
2008 + // Basic validation
2009 + if (!$post || wp_is_post_revision($post_id)) {
2010 + return;
2011 + }
2012 +
2013 + $post_type = $post->post_type;
2014 +
2015 + // Check if sync is enabled for this post type
2016 + $should_sync = false;
2017 +
2018 + // Check built-in post types first
2019 + if ($post_type === 'post' && get_option('mxchat_auto_sync_posts') === '1') {
2020 + $should_sync = true;
2021 + } else if ($post_type === 'page' && get_option('mxchat_auto_sync_pages') === '1') {
2022 + $should_sync = true;
2023 + } else {
2024 + // Check custom post types
2025 + $option_name = 'mxchat_auto_sync_' . $post_type;
2026 + if (get_option($option_name) === '1') {
2027 + $should_sync = true;
2028 + }
2029 + }
2030 +
2031 + if (!$should_sync) {
2032 + return;
2033 + }
2034 +
2035 + // Get the URL before post is deleted
2036 + $source_url = get_permalink($post_id);
2037 + if (!$source_url) {
2038 + //error_log('MXChat: Failed to get permalink for post ' . $post_id);
2039 + return;
2040 + }
2041 +
2042 + // Check for Pinecone addon and its settings
2043 + $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2044 + $use_pinecone = false;
2045 + if ($pinecone_settings && is_array($pinecone_settings)) {
2046 + $use_pinecone = (
2047 + isset($pinecone_settings['mxchat_use_pinecone']) &&
2048 + $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2049 + !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2050 + !empty($pinecone_settings['mxchat_pinecone_host']) &&
2051 + !empty($pinecone_settings['mxchat_pinecone_index']) &&
2052 + !empty($pinecone_settings['mxchat_pinecone_environment'])
2053 + );
2054 + }
2055 +
2056 + $deletion_successful = false;
2057 +
2058 + if ($use_pinecone) {
2059 + try {
2060 + $pinecone_result = $this->delete_from_pinecone(
2061 + array($source_url),
2062 + $pinecone_settings['mxchat_pinecone_api_key'],
2063 + $pinecone_settings['mxchat_pinecone_environment'],
2064 + $pinecone_settings['mxchat_pinecone_index']
2065 + );
2066 +
2067 + if (!$pinecone_result['success']) {
2068 + //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
2069 + } else {
2070 + $deletion_successful = true;
2071 + }
2072 + } catch (Exception $e) {
2073 + //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
2074 + }
2075 + }
2076 +
2077 + // Always attempt WordPress DB deletion, regardless of Pinecone status
2078 + try {
2079 + global $wpdb;
2080 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2081 +
2082 + $result = $wpdb->delete(
2083 + $table_name,
2084 + array('source_url' => $source_url),
2085 + array('%s')
2086 + );
2087 +
2088 + if ($result === false) {
2089 + //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
2090 + } else {
2091 + $deletion_successful = true;
2092 + }
2093 + } catch (Exception $e) {
2094 + //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
2095 + }
2096 +
2097 + if (!$deletion_successful) {
2098 + //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
2099 + } else {
2100 +
2101 + }
2102 +}
2103 +
2104 +
2105 +
2106 +
2107 +
2108 +
2109 +
1275 2110 // Modified storage function to add type field
1276 2111 private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1277 2112 $vector_id = $vector_id ?: md5($url);
1278 2113 $options = get_option('mxchat_pinecone_addon_options');
@@ -1339,8 +2174,10 @@
1339 2174 'success' => true,
1340 2175 'message' => 'Successfully stored in Pinecone'
1341 2176 );
1342 2177 }
2178 +
2179 +
1343 2180 // Helper method for WordPress DB storage
1344 2181 private function store_in_wordpress_db($content, $url, $embedding_vector) {
1345 2182 global $wpdb;
1346 2183 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
@@ -1372,103 +2209,10 @@
1372 2209 array('%s', '%s', '%s', '%s')
1373 2210 );
1374 2211 }
1375 2212 }
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 2213
1386 - // Basic validation
1387 - if (!$post || wp_is_post_revision($post_id)) {
1388 - return;
1389 - }
1390 2214
1391 - // Check post type
1392 - $post_type = $post->post_type;
1393 - if (!in_array($post_type, array('post', 'page'))) {
1394 - return;
1395 - }
1396 -
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 - }
1402 -
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 - }
1409 -
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 - }
1423 -
1424 - $deletion_successful = false;
1425 -
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 - );
1434 -
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;
1439 - }
1440 - } catch (Exception $e) {
1441 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1442 - }
1443 - }
1444 -
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 - }
1468 -}
1469 -
1470 -
1471 2215 public function mxchat_handle_content_submission() {
1472 2216 // Check if the form was submitted and the user has permission.
1473 2217 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1474 2218 return;
@@ -1640,31 +2384,57 @@
1640 2384 $start_page = absint($status['processed_pages']);
1641 2385 $end_page = min($start_page + $batch_size, $total_pages);
1642 2386
1643 2387 $instance = new self(); // Create an instance of the class
2388 + $options = get_option('mxchat_options');
2389 +
2390 + if (empty($options['api_key'])) {
2391 + throw new Exception('API key is missing or invalid');
2392 + }
1644 2393
1645 2394 for ($i = $start_page; $i < $end_page; $i++) {
1646 2395 $text = $pages[$i]->getText();
2396 +
2397 + if (empty($text)) {
2398 + // Log empty page but continue processing
2399 + //error_log(sprintf('[MXCHAT-PDF] Warning: Empty text on page %d of %s', $i + 1, $pdf_url));
2400 + continue;
2401 + }
2402 +
1647 2403 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1648 2404
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 - );
2405 + if (empty($sanitized_content)) {
2406 + // Log empty sanitized content but continue processing
2407 + //error_log(sprintf('[MXCHAT-PDF] Warning: No valid content after sanitization on page %d of %s', $i + 1, $pdf_url));
2408 + continue;
2409 + }
1660 2410
1661 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1662 - $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
2411 + $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
2412 +
2413 + if (!is_array($embedding_vector)) {
2414 + // If embedding generation fails, log error and throw exception
2415 + $error_msg = is_string($embedding_vector) ? $embedding_vector : 'Unknown embedding generation error';
2416 + //error_log(sprintf('[MXCHAT-PDF] Error generating embedding for page %d: %s', $i + 1, $error_msg));
2417 + throw new Exception(sprintf('Failed to generate embedding for page %d: %s', $i + 1, $error_msg));
2418 + }
2419 +
2420 + $metadata = array(
2421 + 'document_type' => 'pdf',
2422 + 'total_pages' => $total_pages,
2423 + 'current_page' => $i + 1,
2424 + 'prev_page' => $i > 0 ? $i : null,
2425 + 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
2426 + 'source_url' => $pdf_url
2427 + );
1663 2428
1664 - $options = get_option('mxchat_options');
1665 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1666 - }
2429 + $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
2430 + $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
2431 +
2432 + $db_result = MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
2433 +
2434 + if (is_wp_error($db_result)) {
2435 + throw new Exception(sprintf('Failed to store content in database for page %d: %s',
2436 + $i + 1, $db_result->get_error_message()));
1667 2437 }
1668 2438
1669 2439 // Update progress with sanitized data
1670 2440 $status['processed_pages'] = absint($i + 1);
@@ -1690,16 +2460,36 @@
1690 2460 }
1691 2461 }
1692 2462
1693 2463 } catch (\Exception $e) {
1694 - $status['status'] = 'error';
1695 - $status['error'] = sanitize_text_field($e->getMessage());
2464 + //error_log(sprintf('[MXCHAT-PDF] Error processing PDF: %s', $e->getMessage()));
2465 +
2466 + // Get current status to update it
2467 + $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
2468 + $status = get_transient($status_key);
2469 +
2470 + if (!$status || !is_array($status)) {
2471 + $status = array(
2472 + 'total_pages' => $total_pages,
2473 + 'processed_pages' => 0,
2474 + 'status' => 'error',
2475 + 'error' => sanitize_text_field($e->getMessage()),
2476 + 'last_update' => time()
2477 + );
2478 + } else {
2479 + $status['status'] = 'error';
2480 + $status['error'] = sanitize_text_field($e->getMessage());
2481 + $status['last_update'] = time();
2482 + }
2483 +
1696 2484 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2485 +
1697 2486 if (file_exists($pdf_path)) {
1698 2487 wp_delete_file($pdf_path);
1699 2488 }
1700 2489 }
1701 2490 }
2491 +
1702 2492 public function get_pdf_processing_status($pdf_url) {
1703 2493 $pdf_url = esc_url_raw($pdf_url);
1704 2494 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1705 2495
@@ -1706,29 +2496,57 @@
1706 2496 if (!$status || !is_array($status)) {
1707 2497 return false;
1708 2498 }
1709 2499
1710 - return array(
2500 + // Check for stalled processing (no updates for 5 minutes)
2501 + if ($status['status'] === 'processing' && (time() - absint($status['last_update'])) > 300) {
2502 + $status['status'] = 'error';
2503 + $status['error'] = __('PDF processing appears to be stalled. No updates for over 5 minutes.', 'mxchat');
2504 +
2505 + // Save the updated status
2506 + set_transient(
2507 + sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
2508 + array_map('sanitize_text_field', $status),
2509 + DAY_IN_SECONDS
2510 + );
2511 + }
2512 +
2513 + $result = array(
1711 2514 'total_pages' => absint($status['total_pages']),
1712 2515 'processed_pages' => absint($status['processed_pages']),
1713 2516 'percentage' => ($status['total_pages'] > 0)
1714 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1715 - : 0,
2517 + ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
2518 + : 0,
1716 2519 'status' => sanitize_text_field($status['status']),
1717 2520 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1718 2521 );
2522 +
2523 + // Add error message if present
2524 + if (isset($status['error']) && !empty($status['error'])) {
2525 + $result['error'] = sanitize_text_field($status['error']);
2526 + }
2527 +
2528 + return $result;
1719 2529 }
2530 +
2531 +
1720 2532 public function mxchat_handle_sitemap_submission() {
2533 + // Start logging the submission process
2534 + //error_log('[MXCHAT-URL] ===== Starting URL submission process =====');
2535 +
1721 2536 // Check if the form was submitted and verify permissions
1722 2537 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
2538 + //error_log('[MXCHAT-URL] Error: Unauthorized access or form not submitted properly');
1723 2539 wp_die(esc_html__('Unauthorized access', 'mxchat'));
1724 2540 }
1725 2541
1726 2542 // Verify nonce
2543 + //error_log('[MXCHAT-URL] Verifying nonce');
1727 2544 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1728 2545
1729 2546 // Validate URL
1730 2547 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
2548 + //error_log('[MXCHAT-URL] Error: Empty or missing URL');
1731 2549 set_transient('mxchat_admin_notice_error',
1732 2550 esc_html__('Please provide a valid URL.', 'mxchat'),
1733 2551 30
1734 2552 );
@@ -1736,12 +2554,40 @@
1736 2554 exit;
1737 2555 }
1738 2556
1739 2557 $submitted_url = esc_url_raw($_POST['sitemap_url']);
2558 + //error_log('[MXCHAT-URL] Processing URL: ' . $submitted_url);
2559 +
2560 + // Validate API key first
2561 + $options = get_option('mxchat_options');
2562 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
2563 +
2564 + if (strpos($selected_model, 'voyage') === 0) {
2565 + $api_key = $options['voyage_api_key'] ?? '';
2566 + $provider_name = 'Voyage AI';
2567 + } else {
2568 + $api_key = $options['api_key'] ?? '';
2569 + $provider_name = 'OpenAI';
2570 + }
2571 +
2572 + if (empty($api_key)) {
2573 + $error_message = sprintf(
2574 + esc_html__('%s API key is not configured. Please add your API key in the settings before submitting content.', 'mxchat'),
2575 + $provider_name
2576 + );
2577 + //error_log('[MXCHAT-URL] Error: ' . $error_message);
2578 + set_transient('mxchat_admin_notice_error', $error_message, 30);
2579 + //error_log('[MXCHAT-URL] Set error transient: ' . $error_message);
2580 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2581 + exit;
2582 + }
2583 +
2584 + //error_log('[MXCHAT-URL] Fetching URL content');
1740 2585 $response = wp_remote_get($submitted_url, array('timeout' => 30));
1741 2586
1742 2587 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');
2588 + $error_message = is_wp_error($response) ? $response->get_error_message() : 'HTTP Status: ' . wp_remote_retrieve_response_code($response);
2589 + //error_log('[MXCHAT-URL] Error fetching URL: ' . $error_message);
1744 2590 set_transient('mxchat_admin_notice_error',
1745 2591 sprintf(
1746 2592 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1747 2593 esc_html($error_message)
@@ -1752,11 +2598,13 @@
1752 2598 exit;
1753 2599 }
1754 2600
1755 2601 $content_type = wp_remote_retrieve_header($response, 'content-type');
2602 + //error_log('[MXCHAT-URL] Content type: ' . $content_type);
1756 2603 $body_content = wp_remote_retrieve_body($response);
1757 2604
1758 2605 if (empty($body_content)) {
2606 + //error_log('[MXCHAT-URL] Error: Empty response body');
1759 2607 set_transient('mxchat_admin_notice_error',
1760 2608 esc_html__('Empty response received from URL.', 'mxchat'),
1761 2609 30
1762 2610 );
@@ -1762,12 +2610,15 @@
1762 2610 );
1763 2611 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1764 2612 exit;
1765 2613 }
2614 + //error_log('[MXCHAT-URL] Retrieved body content length: ' . strlen($body_content) . ' bytes');
1766 2615
1767 2616 // Handle PDF URL
1768 2617 if ($this->is_pdf_url($submitted_url, $response)) {
2618 + //error_log('[MXCHAT-URL] Detected PDF URL, handling PDF for knowledge base');
1769 2619 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
2620 + //error_log('[MXCHAT-URL] PDF handling result: ' . $result);
1770 2621
1771 2622 if ($result === 'scheduled') {
1772 2623 set_transient(
1773 2624 'mxchat_last_pdf_url',
@@ -1779,9 +2630,9 @@
1779 2630 30
1780 2631 );
1781 2632 } else {
1782 2633 set_transient('mxchat_admin_notice_error',
1783 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
2634 + esc_html__('Failed to start PDF processing: ', 'mxchat') . esc_html($result),
1784 2635 30
1785 2636 );
1786 2637 }
1787 2638
@@ -1790,8 +2641,9 @@
1790 2641 }
1791 2642
1792 2643 // Handle Sitemap XML
1793 2644 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
2645 + //error_log('[MXCHAT-URL] Detected XML content, processing as sitemap');
1794 2646 libxml_use_internal_errors(true);
1795 2647 $xml = simplexml_load_string($body_content);
1796 2648 $xml_errors = libxml_get_errors();
1797 2649 libxml_clear_errors();
@@ -1796,8 +2648,15 @@
1796 2648 $xml_errors = libxml_get_errors();
1797 2649 libxml_clear_errors();
1798 2650
1799 2651 if ($xml === false || !empty($xml_errors)) {
2652 + //error_log('[MXCHAT-URL] Error: Invalid XML format');
2653 + if (!empty($xml_errors)) {
2654 + foreach ($xml_errors as $error) {
2655 + //error_log('[MXCHAT-URL] XML Error: ' . $error->message);
2656 + }
2657 + }
2658 +
1800 2659 set_transient('mxchat_admin_notice_error',
1801 2660 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1802 2661 30
1803 2662 );
@@ -1804,9 +2663,11 @@
1804 2663 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1805 2664 exit;
1806 2665 }
1807 2666
2667 + //error_log('[MXCHAT-URL] Valid XML found, handling sitemap for knowledge base');
1808 2668 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
2669 + //error_log('[MXCHAT-URL] Sitemap handling result: ' . $result);
1809 2670
1810 2671 if ($result === 'scheduled') {
1811 2672 set_transient(
1812 2673 'mxchat_last_sitemap_url',
@@ -1817,10 +2678,12 @@
1817 2678 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1818 2679 30
1819 2680 );
1820 2681 } else {
2682 + // Return to the admin page without a redirect for better error display
2683 + // The error is already stored in the sitemap status transient
1821 2684 set_transient('mxchat_admin_notice_error',
1822 - esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
2685 + esc_html__('Failed to start sitemap processing. Please check the status below for details.', 'mxchat'),
1823 2686 30
1824 2687 );
1825 2688 }
1826 2689
@@ -1828,42 +2691,147 @@
1828 2691 exit;
1829 2692 }
1830 2693
1831 2694 // Handle Regular URL
1832 - $page_content = $this->mxchat_extract_main_content($body_content);
1833 - $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2695 + // Handle Regular URL
2696 +//error_log('[MXCHAT-URL] Processing as regular webpage');
2697 +$page_content = $this->mxchat_extract_main_content($body_content);
2698 +//error_log('[MXCHAT-URL] Extracted content length: ' . strlen($page_content) . ' bytes');
1834 2699
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 - );
2700 +$sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2701 +//error_log('[MXCHAT-URL] Sanitized content length: ' . strlen($sanitized_content) . ' bytes');
2702 +
2703 +if (empty($sanitized_content)) {
2704 + //error_log('[MXCHAT-URL] Error: No valid content after sanitization');
2705 +
2706 + // Set both transients - the error notice and the URL status
2707 + set_transient('mxchat_admin_notice_error',
2708 + esc_html__('No valid content found on the provided URL.', 'mxchat'),
2709 + 30
2710 + );
2711 +
2712 + // Set URL status transient
2713 + set_transient('mxchat_single_url_status', [
2714 + 'url' => $submitted_url,
2715 + 'timestamp' => current_time('mysql'),
2716 + 'status' => 'failed',
2717 + 'error' => esc_html__('No valid content found on the provided URL.', 'mxchat')
2718 + ], DAY_IN_SECONDS);
2719 +
2720 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2721 + exit;
2722 +}
2723 +
2724 +//error_log('[MXCHAT-URL] Generating embedding for content');
2725 +$embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2726 +
2727 +// Check if embedding_vector is a string (error message)
2728 +if (is_string($embedding_vector)) {
2729 + //error_log('[MXCHAT-URL] Error generating embedding: ' . $embedding_vector);
2730 + $error_message = esc_html__('Failed to generate embedding: ', 'mxchat') . esc_html($embedding_vector);
2731 + //error_log('[MXCHAT-URL] Setting error transient: ' . $error_message);
2732 +
2733 + // Set both transients
2734 + set_transient('mxchat_admin_notice_error', $error_message, 30);
2735 +
2736 + // Set URL status transient
2737 + set_transient('mxchat_single_url_status', [
2738 + 'url' => $submitted_url,
2739 + 'timestamp' => current_time('mysql'),
2740 + 'status' => 'failed',
2741 + 'error' => $error_message
2742 + ], DAY_IN_SECONDS);
2743 +
2744 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2745 + exit;
2746 +}
2747 +
2748 +if (is_array($embedding_vector)) {
2749 + //error_log('[MXCHAT-URL] Successfully generated embedding with ' . count($embedding_vector) . ' dimensions');
2750 +
2751 + $db_result = MxChat_Utils::submit_content_to_db(
2752 + $sanitized_content,
2753 + $submitted_url,
2754 + $this->options['api_key']
2755 + );
2756 +
2757 + if (is_wp_error($db_result)) {
2758 + //error_log('[MXCHAT-URL] Error: Failed to store content in database: ' . $db_result->get_error_message());
2759 + $error_message = esc_html__('Failed to store content in database: ', 'mxchat') . esc_html($db_result->get_error_message());
2760 + //error_log('[MXCHAT-URL] Setting error transient: ' . $error_message);
2761 +
2762 + // Set both transients
2763 + set_transient('mxchat_admin_notice_error', $error_message, 30);
2764 +
2765 + // Set URL status transient
2766 + set_transient('mxchat_single_url_status', [
2767 + 'url' => $submitted_url,
2768 + 'timestamp' => current_time('mysql'),
2769 + 'status' => 'failed',
2770 + 'error' => $error_message
2771 + ], DAY_IN_SECONDS);
2772 +
1840 2773 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1841 2774 exit;
1842 2775 }
1843 2776
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 - );
2777 + //error_log('[MXCHAT-URL] Successfully stored content in database');
2778 + $success_message = esc_html__('URL content successfully submitted!', 'mxchat');
2779 + //error_log('[MXCHAT-URL] Setting success transient: ' . $success_message);
2780 +
2781 + // Set both transients
2782 + set_transient('mxchat_admin_notice_success', $success_message, 30);
2783 +
2784 + // Set URL status transient with success
2785 + set_transient('mxchat_single_url_status', [
2786 + 'url' => $submitted_url,
2787 + 'timestamp' => current_time('mysql'),
2788 + 'status' => 'complete',
2789 + 'content_length' => strlen($sanitized_content),
2790 + 'embedding_dimensions' => count($embedding_vector)
2791 + ], DAY_IN_SECONDS);
2792 +
2793 +} else {
2794 + //error_log('[MXCHAT-URL] Error: Failed to generate embedding. Unexpected result type: ' . gettype($embedding_vector));
2795 + $error_message = esc_html__('Failed to generate embedding: Unexpected result type. Please check your API key and try again.', 'mxchat');
2796 + //error_log('[MXCHAT-URL] Setting error transient: ' . $error_message);
2797 +
2798 + // Set both transients
2799 + set_transient('mxchat_admin_notice_error', $error_message, 30);
2800 +
2801 + // Set URL status transient
2802 + set_transient('mxchat_single_url_status', [
2803 + 'url' => $submitted_url,
2804 + 'timestamp' => current_time('mysql'),
2805 + 'status' => 'failed',
2806 + 'error' => $error_message
2807 + ], DAY_IN_SECONDS);
2808 +}
2809 +
2810 +//error_log('[MXCHAT-URL] ===== Completed URL submission process =====');
2811 +wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2812 +exit;
2813 +}
2814 +/**
2815 + * Get the status of the last single URL submission
2816 + */
2817 +private function get_single_url_status() {
2818 + $status = get_transient('mxchat_single_url_status');
2819 + if (!$status) {
2820 + return null;
1860 2821 }
2822 +
2823 + // Add human-readable time
2824 + if (isset($status['timestamp'])) {
2825 + $status['human_time'] = human_time_diff(strtotime($status['timestamp']), current_time('timestamp')) . ' ' . __('ago', 'mxchat');
2826 + }
2827 +
2828 + return $status;
2829 +}
1861 2830
1862 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1863 - exit;
1864 -}
1865 2831 private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
2832 + // Clear any single URL status when starting sitemap processing
2833 + delete_transient('mxchat_single_url_status');
1866 2834 if (!current_user_can('manage_options')) {
1867 2835 //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
1868 2836 return false;
1869 2837 }
@@ -1874,8 +2842,40 @@
1874 2842 if (!$xml || !is_object($xml)) {
1875 2843 throw new Exception(__('Invalid XML object provided', 'mxchat'));
1876 2844 }
1877 2845
2846 + // Add embedding validation before processing
2847 + // Test embedding with a small sample text to verify API key is working
2848 + $test_result = $this->mxchat_generate_embedding("This is a test to verify the embedding API key is working.");
2849 +
2850 + // Check if test_result is a string (error message) rather than an array (valid embedding)
2851 + if (is_string($test_result)) {
2852 + //error_log('[MXCHAT-URL] Embedding API validation failed: ' . $test_result);
2853 +
2854 + // Store the error in the status transient so it can be displayed later
2855 + $status_data = array(
2856 + 'total_urls' => 0,
2857 + 'processed_urls' => 0,
2858 + 'status' => 'error',
2859 + 'error' => __('Embedding API validation failed: ', 'mxchat') . $test_result,
2860 + 'last_update' => time()
2861 + );
2862 +
2863 + set_transient(
2864 + sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
2865 + array_map('sanitize_text_field', $status_data),
2866 + DAY_IN_SECONDS
2867 + );
2868 +
2869 + throw new Exception(__('Embedding API validation failed: ', 'mxchat') . $test_result);
2870 + }
2871 +
2872 + // Make sure it's an array (valid embedding)
2873 + if (!is_array($test_result)) {
2874 + //error_log('[MXCHAT-URL] Embedding API returned unexpected result type: ' . gettype($test_result));
2875 + throw new Exception(__('Embedding API returned unexpected result type. Please check your configuration.', 'mxchat'));
2876 + }
2877 +
1878 2878 $urls = [];
1879 2879 foreach ($xml->url as $url_element) {
1880 2880 $url = esc_url_raw((string)$url_element->loc);
1881 2881 if ($url) {
@@ -1912,12 +2912,36 @@
1912 2912
1913 2913 return __('scheduled', 'mxchat');
1914 2914
1915 2915 } catch (\Exception $e) {
1916 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1917 - return false;
2916 + $error_message = $e->getMessage();
2917 + //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($error_message)));
2918 +
2919 + // Store the sitemap URL and error in transients so they can be displayed
2920 + set_transient(
2921 + 'mxchat_last_sitemap_url',
2922 + sanitize_text_field($sitemap_url),
2923 + DAY_IN_SECONDS
2924 + );
2925 +
2926 + $status_data = array(
2927 + 'total_urls' => 0,
2928 + 'processed_urls' => 0,
2929 + 'status' => 'error',
2930 + 'error' => $error_message,
2931 + 'last_update' => time()
2932 + );
2933 +
2934 + set_transient(
2935 + sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
2936 + array_map('sanitize_text_field', $status_data),
2937 + DAY_IN_SECONDS
2938 + );
2939 +
2940 + return $error_message;
1918 2941 }
1919 2942 }
2943 +
1920 2944 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1921 2945 // Validate inputs
1922 2946 $sitemap_url = esc_url_raw($sitemap_url);
1923 2947 $total_urls = absint($total_urls);
@@ -1935,8 +2959,13 @@
1935 2959 if (!$status || !is_array($status)) {
1936 2960 throw new Exception('Invalid status data retrieved from transient');
1937 2961 }
1938 2962
2963 + // Initialize failed_urls array if it doesn't exist
2964 + if (!isset($status['failed_urls_list']) || !is_array($status['failed_urls_list'])) {
2965 + $status['failed_urls_list'] = [];
2966 + }
2967 +
1939 2968 $start_url = absint($status['processed_urls']);
1940 2969 $end_url = min($start_url + $batch_size, $total_urls);
1941 2970 $instance = new self();
1942 2971
@@ -1943,11 +2972,38 @@
1943 2972 // Track failures in batch
1944 2973 $batch_stats = [
1945 2974 'processed' => 0,
1946 2975 'failed' => 0,
1947 - 'last_error' => ''
2976 + 'last_error' => '',
2977 + 'embedding_errors' => 0 // Track specifically embedding errors
1948 2978 ];
1949 2979
2980 + // Check embedding configuration with first URL
2981 + if ($start_url === 0) {
2982 + $page_url = esc_url_raw($urls[0]);
2983 + $page_response = wp_remote_get($page_url);
2984 +
2985 + if (!is_wp_error($page_response) && wp_remote_retrieve_response_code($page_response) === 200) {
2986 + $page_html = wp_remote_retrieve_body($page_response);
2987 + $page_content = $instance->mxchat_extract_main_content($page_html);
2988 + $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2989 +
2990 + if (!empty($sanitized_content)) {
2991 + $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2992 +
2993 + // Check if embedding_vector is a string (error message)
2994 + if (is_string($embedding_vector)) {
2995 + throw new Exception('Embedding generation failed: ' . $embedding_vector);
2996 + }
2997 +
2998 + // Make sure it's an array (valid embedding)
2999 + if (!is_array($embedding_vector)) {
3000 + throw new Exception('Embedding generation returned unexpected result type: ' . gettype($embedding_vector));
3001 + }
3002 + }
3003 + }
3004 + }
3005 +
1950 3006 for ($i = $start_url; $i < $end_url; $i++) {
1951 3007 $page_url = esc_url_raw($urls[$i]);
1952 3008 $page_response = wp_remote_get($page_url);
1953 3009
@@ -1952,8 +3008,18 @@
1952 3008 $page_response = wp_remote_get($page_url);
1953 3009
1954 3010 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1955 3011 $batch_stats['failed']++;
3012 + $error_message = is_wp_error($page_response) ? $page_response->get_error_message() : 'HTTP Status: ' . wp_remote_retrieve_response_code($page_response);
3013 + $batch_stats['last_error'] = 'Failed to fetch URL: ' . $error_message;
3014 +
3015 + // Add to failed URLs list with error message
3016 + $status['failed_urls_list'][] = [
3017 + 'url' => $page_url,
3018 + 'error' => $error_message,
3019 + 'time' => time()
3020 + ];
3021 +
1956 3022 continue;
1957 3023 }
1958 3024
1959 3025 $page_html = wp_remote_retrieve_body($page_response);
@@ -1962,8 +3028,29 @@
1962 3028
1963 3029 if (!empty($sanitized_content)) {
1964 3030 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
1965 3031
3032 + // Check if embedding_vector is a string (error message)
3033 + if (is_string($embedding_vector)) {
3034 + $batch_stats['failed']++;
3035 + $batch_stats['embedding_errors']++;
3036 + $batch_stats['last_error'] = 'Failed to generate embedding: ' . $embedding_vector;
3037 +
3038 + // Add to failed URLs list with error message
3039 + $status['failed_urls_list'][] = [
3040 + 'url' => $page_url,
3041 + 'error' => 'Embedding error: ' . $embedding_vector,
3042 + 'time' => time()
3043 + ];
3044 +
3045 + // If we have multiple embedding errors, stop processing
3046 + if ($batch_stats['embedding_errors'] >= 10) {
3047 + throw new Exception('Multiple embedding failures detected: ' . $embedding_vector);
3048 + }
3049 + continue;
3050 + }
3051 +
3052 + // Check if it's an array (valid embedding)
1966 3053 if (is_array($embedding_vector)) {
1967 3054 $options = get_option('mxchat_options');
1968 3055 $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
1969 3056
@@ -1969,8 +3056,16 @@
1969 3056
1970 3057 if (is_wp_error($submission_result)) {
1971 3058 $batch_stats['failed']++;
1972 3059 $batch_stats['last_error'] = $submission_result->get_error_message();
3060 +
3061 + // Add to failed URLs list with error message
3062 + $status['failed_urls_list'][] = [
3063 + 'url' => $page_url,
3064 + 'error' => 'Database submission error: ' . $submission_result->get_error_message(),
3065 + 'time' => time()
3066 + ];
3067 +
1973 3068 continue;
1974 3069 }
1975 3070
1976 3071 $batch_stats['processed']++;
@@ -1975,9 +3070,22 @@
1975 3070
1976 3071 $batch_stats['processed']++;
1977 3072 } else {
1978 3073 $batch_stats['failed']++;
1979 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
3074 + $batch_stats['embedding_errors']++;
3075 + $batch_stats['last_error'] = 'Failed to generate embedding: Unexpected result type: ' . gettype($embedding_vector);
3076 +
3077 + // Add to failed URLs list with error message
3078 + $status['failed_urls_list'][] = [
3079 + 'url' => $page_url,
3080 + 'error' => 'Embedding error: Unexpected result type: ' . gettype($embedding_vector),
3081 + 'time' => time()
3082 + ];
3083 +
3084 + // If we have multiple embedding errors, stop processing
3085 + if ($batch_stats['embedding_errors'] >= 10) {
3086 + throw new Exception('Multiple embedding failures detected. Please check your embedding API configuration.');
3087 + }
1980 3088 }
1981 3089 }
1982 3090
1983 3091 $status['processed_urls'] = absint($i + 1);
@@ -1984,9 +3092,14 @@
1984 3092 $status['last_update'] = time();
1985 3093 $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
1986 3094 $status['last_error'] = $batch_stats['last_error'];
1987 3095
1988 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
3096 + // Limit the number of failed URLs we store to prevent transient size issues
3097 + if (count($status['failed_urls_list']) > 100) {
3098 + $status['failed_urls_list'] = array_slice($status['failed_urls_list'], -100);
3099 + }
3100 +
3101 + set_transient($status_key, $status, DAY_IN_SECONDS);
1989 3102 }
1990 3103
1991 3104 // If all URLs in this batch failed, stop processing
1992 3105 if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
@@ -1995,15 +3108,15 @@
1995 3108 'Processing stopped: %d consecutive failures. Last error: %s',
1996 3109 $batch_stats['failed'],
1997 3110 $batch_stats['last_error']
1998 3111 );
1999 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
3112 + set_transient($status_key, $status, DAY_IN_SECONDS);
2000 3113 return;
2001 3114 }
2002 3115
2003 3116 if ($end_url < $total_urls) {
2004 3117 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2005 - 'urls' => array_map('esc_url_raw', $urls),
3118 + 'urls' => $urls,
2006 3119 'sitemap_url' => $sitemap_url,
2007 3120 'total_urls' => $total_urls,
2008 3121 'batch_size' => $batch_size,
2009 3122 'batch_pause' => $batch_pause,
@@ -2009,17 +3122,249 @@
2009 3122 'batch_pause' => $batch_pause,
2010 3123 ));
2011 3124 } else {
2012 3125 $status['status'] = 'complete';
2013 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
3126 + set_transient($status_key, $status, DAY_IN_SECONDS);
2014 3127 }
2015 3128 } catch (\Exception $e) {
2016 3129 $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);
3130 + $status['error'] = $e->getMessage();
3131 + set_transient($status_key, $status, DAY_IN_SECONDS);
2019 3132 }
2020 3133 }
2021 3134
3135 +private function mxchat_sanitize_content_for_api($content) {
3136 + //error_log('[MXCHAT-SANITIZE] Original content preview: ' . substr($content, 0, 500) . '...');
3137 +
3138 + // Remove script, style tags, and HTML comments
3139 + $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
3140 + $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
3141 + $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
3142 +
3143 + // Remove all HTML tags and decode HTML entities
3144 + $content = wp_strip_all_tags($content);
3145 + $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
3146 +
3147 + // Trim and normalize whitespace
3148 + $content = trim(preg_replace('/\s+/', ' ', $content));
3149 +
3150 + // Remove control characters (which can cause database issues)
3151 + $content = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $content);
3152 +
3153 + // Remove NULL bytes which can cause database errors
3154 + $content = str_replace("\0", "", $content);
3155 +
3156 + // Ensure valid UTF-8 encoding
3157 + $content = wp_check_invalid_utf8($content);
3158 +
3159 + // Remove any extremely long strings without spaces (often garbage)
3160 + $content = preg_replace('/\S{300,}/', ' ', $content);
3161 +
3162 + // Replace problematic characters that often cause database issues
3163 + $content = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $content); // Remove emoji and other high Unicode characters
3164 +
3165 + // Replace any remaining potentially problematic characters with spaces
3166 + $content = preg_replace('/[^\p{L}\p{N}\p{P}\p{Z}\p{Sm}]/u', ' ', $content);
3167 +
3168 + // Limit to reasonable length if needed
3169 + $max_length = 65000; // Just under MySQL TEXT field limit
3170 + if (strlen($content) > $max_length) {
3171 + $content = substr($content, 0, $max_length);
3172 + }
3173 +
3174 + //error_log('[MXCHAT-SANITIZE] Sanitized content preview: ' . substr($content, 0, 500) . '...');
3175 + return $content;
3176 +}
3177 +private static function mxchat_extract_main_content($html) {
3178 + if (empty($html)) {
3179 + return '';
3180 + }
3181 + try {
3182 + $dom = new DOMDocument;
3183 + libxml_use_internal_errors(true); // Suppress HTML parsing errors
3184 + @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
3185 + $xpath = new DOMXPath($dom);
3186 +
3187 + // For debugging purposes
3188 + $debugEnabled = false; // Set to true to enable debugging output
3189 + $debug = function($message) use ($debugEnabled) {
3190 + if ($debugEnabled) {
3191 + //error_log('[MXCHAT-DEBUG] ' . $message);
3192 + }
3193 + };
3194 +
3195 + // Direct targeting for Gerow theme posts
3196 + $post_text = $xpath->query('//div[contains(@class, "post-text")]');
3197 + if ($post_text && $post_text->length > 0) {
3198 + $debug("Found post-text directly");
3199 + $content = '';
3200 + foreach ($post_text as $node) {
3201 + $content .= $dom->saveHTML($node);
3202 + }
3203 + if (!empty($content)) {
3204 + $debug("Returning post-text content");
3205 + return $content;
3206 + }
3207 + }
3208 +
3209 + // Try to get the blog details content which contains the post-text
3210 + $blog_details = $xpath->query('//div[contains(@class, "blog-details-content")]');
3211 + if ($blog_details && $blog_details->length > 0) {
3212 + $debug("Found blog-details-content");
3213 + $content = '';
3214 + foreach ($blog_details as $node) {
3215 + $content .= $dom->saveHTML($node);
3216 + }
3217 + if (!empty($content)) {
3218 + $debug("Returning blog-details-content");
3219 + return $content;
3220 + }
3221 + }
3222 +
3223 + // Try to get the article which contains the blog details
3224 + $article = $xpath->query('//article[contains(@class, "blog-details-wrap")]');
3225 + if ($article && $article->length > 0) {
3226 + $debug("Found article with blog-details-wrap");
3227 + $content = '';
3228 + foreach ($article as $node) {
3229 + $content .= $dom->saveHTML($node);
3230 + }
3231 + if (!empty($content)) {
3232 + $debug("Returning article content");
3233 + return $content;
3234 + }
3235 + }
3236 +
3237 + // Try even broader with the blog-item-wrap
3238 + $blog_item = $xpath->query('//div[contains(@class, "blog-item-wrap")]');
3239 + if ($blog_item && $blog_item->length > 0) {
3240 + $debug("Found blog-item-wrap");
3241 + $content = '';
3242 + foreach ($blog_item as $node) {
3243 + $content .= $dom->saveHTML($node);
3244 + }
3245 + if (!empty($content)) {
3246 + $debug("Returning blog-item-wrap content");
3247 + return $content;
3248 + }
3249 + }
3250 +
3251 + // Specific Gerow theme path
3252 + $gerow_path = $xpath->query('//section[contains(@class, "blog-area")]//div[contains(@class, "post-text")]');
3253 + if ($gerow_path && $gerow_path->length > 0) {
3254 + $debug("Found Gerow theme path to post-text");
3255 + $content = '';
3256 + foreach ($gerow_path as $node) {
3257 + $content .= $dom->saveHTML($node);
3258 + }
3259 + if (!empty($content)) {
3260 + $debug("Returning Gerow post-text content");
3261 + return $content;
3262 + }
3263 + }
3264 +
3265 + // Generic blog post selectors
3266 + $selectors = [
3267 + // Blog post specific selectors
3268 + '//div[contains(@class, "post-text")]',
3269 + '//article[contains(@class, "blog-post-item")]//div[contains(@class, "post-text")]',
3270 + '//div[contains(@class, "blog-details-content")]',
3271 + '//article[contains(@class, "blog-details-wrap")]',
3272 + '//div[contains(@class, "entry-content")]',
3273 + '//div[contains(@class, "blog-content")]',
3274 + '//div[contains(@class, "blog-item-wrap")]',
3275 +
3276 + // More general content selectors
3277 + '//div[contains(@class, "page__content")]',
3278 + '//div[contains(@class, "elementor-widget-container")]',
3279 + '//div[contains(@class, "elementor-text-editor")]',
3280 + '//div[contains(@class, "elementor-widget-text-editor")]',
3281 + '//*[contains(@class, "entry-content")]',
3282 + '//*[contains(@class, "post-content")]',
3283 + '//*[contains(@class, "article-content")]',
3284 + '//*[@id="content"]',
3285 + '//*[@id="main-content"]',
3286 + '//section[contains(@class, "blog-area")]',
3287 + '//article',
3288 + '//main',
3289 + '//div[contains(@class, "content")]'
3290 + ];
3291 +
3292 + // First handle Elementor content
3293 + $debug("Checking for Elementor content");
3294 + $elementor_widgets = $xpath->query('//div[contains(@class, "elementor-element")]//div[contains(@class, "elementor-widget-container")]');
3295 + if ($elementor_widgets && $elementor_widgets->length > 0) {
3296 + $debug("Found Elementor widgets");
3297 + $combined_content = '';
3298 + foreach ($elementor_widgets as $widget) {
3299 + $widget_content = $dom->saveHTML($widget);
3300 + if (!empty($widget_content)) {
3301 + $combined_content .= $widget_content;
3302 + }
3303 + }
3304 + if (!empty($combined_content)) {
3305 + $debug("Returning Elementor content");
3306 + return $combined_content;
3307 + }
3308 + }
3309 +
3310 + // Try standard selectors one by one
3311 + foreach ($selectors as $selector) {
3312 + $debug("Trying selector: " . $selector);
3313 + $nodes = $xpath->query($selector);
3314 + if ($nodes && $nodes->length > 0) {
3315 + $debug("Found matches for selector: " . $selector);
3316 + $content = '';
3317 + foreach ($nodes as $node) {
3318 + $content .= $dom->saveHTML($node);
3319 + }
3320 + if (!empty($content)) {
3321 + $debug("Returning content from selector: " . $selector);
3322 + return $content;
3323 + }
3324 + }
3325 + }
3326 +
3327 + // Manual regex fallback for post-text if DOM methods fail
3328 + $debug("Trying regex fallback");
3329 + if (preg_match('/<div class="post-text">(.*?)<\/div>\s*<\/div>\s*<\/div>/s', $html, $matches)) {
3330 + $debug("Found post-text via regex");
3331 + return '<div class="post-text">' . $matches[1] . '</div>';
3332 + }
3333 +
3334 + // Try to extract the blog section as a whole
3335 + $blog_section = $xpath->query('//section[contains(@class, "blog-area")]');
3336 + if ($blog_section && $blog_section->length > 0) {
3337 + $debug("Found blog-area section");
3338 + $content = '';
3339 + foreach ($blog_section as $node) {
3340 + $content .= $dom->saveHTML($node);
3341 + }
3342 + if (!empty($content)) {
3343 + $debug("Returning blog-area section content");
3344 + return $content;
3345 + }
3346 + }
3347 +
3348 + // Fallback: Return the body content if no specific selector matches
3349 + $debug("Using body fallback");
3350 + $body = $dom->getElementsByTagName('body');
3351 + if ($body->length > 0) {
3352 + return $dom->saveHTML($body->item(0));
3353 + }
3354 +
3355 + // Last resort: return the original HTML
3356 + $debug("Returning original HTML");
3357 + return $html;
3358 + } catch (Exception $e) {
3359 + //error_log('[MXCHAT-ERROR] Content extraction failed: ' . $e->getMessage());
3360 + return $html; // Return original HTML if parsing fails
3361 + } finally {
3362 + libxml_clear_errors();
3363 + }
3364 +}
3365 +
3366 +
2022 3367 public function get_sitemap_processing_status($sitemap_url) {
2023 3368 $sitemap_url = esc_url_raw($sitemap_url);
2024 3369 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2025 3370
@@ -2031,17 +3376,53 @@
2031 3376 'total_urls' => absint($status['total_urls']),
2032 3377 'processed_urls' => absint($status['processed_urls']),
2033 3378 'failed_urls' => absint($status['failed_urls'] ?? 0),
2034 3379 'percentage' => ($status['total_urls'] > 0)
2035 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2036 - : 0,
3380 + ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
3381 + : 0,
2037 3382 'status' => sanitize_text_field($status['status']),
2038 3383 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2039 3384 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2040 - 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
3385 + 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : '',
3386 + 'failed_urls_list' => isset($status['failed_urls_list']) ? $status['failed_urls_list'] : array()
2041 3387 );
2042 3388 }
3389 +public function ajax_get_status_updates() {
3390 + // Verify the request
3391 + check_ajax_referer('mxchat_status_nonce', 'nonce');
2043 3392
3393 + // Get the status just like in your admin page
3394 + $pdf_url = get_transient('mxchat_last_pdf_url');
3395 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
3396 + $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
3397 + $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
3398 +
3399 + // Check for true processing status, not just presence of status
3400 + $is_active_processing =
3401 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
3402 + ($pdf_status && $pdf_status['status'] === 'processing');
3403 +
3404 + // Get single URL status, but only if no sitemap/PDF is processing
3405 + $single_url_status = !$is_active_processing ? $this->get_single_url_status() : false;
3406 +
3407 + // Only clear transients for completed processes, not error states
3408 + if ($pdf_status && $pdf_status['status'] === 'complete') {
3409 + delete_transient('mxchat_last_pdf_url');
3410 + $pdf_status = false;
3411 + }
3412 + if ($sitemap_status && $sitemap_status['status'] === 'complete') {
3413 + delete_transient('mxchat_last_sitemap_url');
3414 + $sitemap_status = false;
3415 + }
3416 +
3417 + // Return JSON response with the status data
3418 + wp_send_json(array(
3419 + 'pdf_status' => $pdf_status,
3420 + 'sitemap_status' => $sitemap_status,
3421 + 'single_url_status' => $single_url_status,
3422 + 'is_processing' => $is_active_processing
3423 + ));
3424 +}
2044 3425 public function mxchat_stop_processing() {
2045 3426 // Verify permissions
2046 3427 if (!current_user_can('manage_options')) {
2047 3428 wp_die(esc_html__('Unauthorized access', 'mxchat'));
@@ -2077,27 +3458,10 @@
2077 3458 );
2078 3459 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2079 3460 exit;
2080 3461 }
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);
2086 3462
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);
2090 3463
2091 - // Trim and normalize whitespace
2092 - $content = trim(preg_replace('/\s+/', ' ', $content));
2093 -
2094 - return $content;
2095 -}
2096 -
2097 -
2098 -
2099 -
2100 3464 public function mxchat_handle_product_change($post_id, $post, $update) {
2101 3465 if ($post->post_type !== 'product') {
2102 3466 return;
2103 3467 }
@@ -2110,9 +3474,8 @@
2110 3474 }
2111 3475 });
2112 3476 }
2113 3477 }
2114 -
2115 3478 private function mxchat_store_product_embedding($product) {
2116 3479 if (!isset($this->options['enable_woocommerce_integration']) ||
2117 3480 !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2118 3481 return;
@@ -2168,9 +3531,8 @@
2168 3531 // Use WordPress DB storage
2169 3532 $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2170 3533 }
2171 3534 }
2172 -
2173 3535 public function mxchat_handle_product_delete($post_id) {
2174 3536 if (get_post_type($post_id) !== 'product') {
2175 3537 return;
2176 3538 }
@@ -2206,8 +3568,9 @@
2206 3568 $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2207 3569 }
2208 3570 }
2209 3571
3572 +
2210 3573 /**
2211 3574 * Delete vectors from Pinecone by source URL
2212 3575 *
2213 3576 * @param array $urls Array of source URLs to delete
@@ -2296,139 +3659,11 @@
2296 3659 );
2297 3660 }
2298 3661
2299 3662
2300 -public function mxchat_fetch_chat_history() {
2301 - global $wpdb;
2302 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2303 3663
2304 - if (!current_user_can('manage_options')) {
2305 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2306 - }
2307 3664
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 3665
2318 - if (empty($session_ids)) {
2319 - wp_die(esc_html__('No chat history available.', 'mxchat'));
2320 - }
2321 -
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 -}
2429 -
2430 -
2431 3666 public function mxchat_create_activation_page() {
2432 3667 $license_status = get_option('mxchat_license_status', 'inactive');
2433 3668 $license_error = get_option('mxchat_license_error', '');
2434 3669 ?>
@@ -2484,9 +3719,9 @@
2484 3719 </div>
2485 3720 <?php
2486 3721 }
2487 3722
2488 -public function mxchat_intents_page_html() {
3723 +public function mxchat_actions_page_html() {
2489 3724 if (!current_user_can('manage_options')) {
2490 3725 return;
2491 3726 }
2492 3727
@@ -2499,9 +3734,9 @@
2499 3734
2500 3735 // Success message
2501 3736 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2502 3737 echo '<div class="notice notice-success is-dismissible"><p>' .
2503 - esc_html__('Intent updated successfully.', 'mxchat') .
3738 + esc_html__('Action updated successfully.', 'mxchat') .
2504 3739 '</p></div>';
2505 3740 }
2506 3741
2507 3742 // Filtering logic
@@ -2522,10 +3757,10 @@
2522 3757 // Pagination
2523 3758 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2524 3759 $total_pages = ceil($total_intents / $per_page);
2525 3760
2526 - // Get intents
2527 - $intents = $wpdb->get_results($wpdb->prepare(
3761 + // Get intents (now called actions)
3762 + $actions = $wpdb->get_results($wpdb->prepare(
2528 3763 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2529 3764 $per_page, $offset
2530 3765 ));
2531 3766
@@ -2530,441 +3765,604 @@
2530 3765 ));
2531 3766
2532 3767 // Get callbacks
2533 3768 $available_callbacks = $this->mxchat_get_available_callbacks();
2534 -
3769 +
2535 3770 ?>
2536 3771 <div class="wrap mxchat-wrapper">
2537 3772 <!-- Hero Section -->
2538 3773 <div class="mxchat-hero">
2539 3774 <h1 class="mxchat-main-title">
2540 - <span class="mxchat-gradient-text">Intent</span> Manager
3775 + <span class="mxchat-gradient-text">Actions</span> Manager
2541 3776 </h1>
2542 3777 <p class="mxchat-hero-subtitle">
2543 - <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
3778 + <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
2544 3779 </p>
2545 3780 </div>
2546 3781
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'); ?>">
3782 + <!-- Actions Header with Search and Filter -->
3783 + <div class="mxchat-actions-header">
3784 + <div class="mxchat-actions-filters">
3785 + <form method="get" class="mxchat-search-form">
3786 + <input type="hidden" name="page" value="mxchat-actions">
3787 + <div class="mxchat-search-group">
3788 + <span class="dashicons dashicons-search"></span>
3789 + <input type="text" name="s" class="mxchat-search-input"
3790 + placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
3791 + value="<?php echo esc_attr($search_term); ?>">
2576 3792 </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'); ?>
3793 + <select name="callback_filter" class="mxchat-action-filter">
3794 + <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
3795 + <?php foreach ($available_callbacks as $function => $callback_data) :
3796 + $label = $callback_data['label']; ?>
3797 + <option value="<?php echo esc_attr($function); ?>"
3798 + <?php selected($callback_filter, $function); ?>>
3799 + <?php echo esc_html($label); ?>
2595 3800 </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'); ?>
3801 + <?php endforeach; ?>
3802 + </select>
3803 + <button type="submit" class="mxchat-button-secondary">
3804 + <?php esc_html_e('Filter', 'mxchat'); ?>
2620 3805 </button>
2621 3806 </form>
2622 3807 </div>
3808 + <div class="mxchat-actions-controls">
3809 + <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
3810 + <span class="dashicons dashicons-plus-alt"></span>
3811 + <?php esc_html_e('Add New Action', 'mxchat'); ?>
3812 + </button>
3813 + </div>
3814 + </div>
2623 3815
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>
3816 + <!-- Actions Grid Layout - All actions in a single grid -->
3817 + <div class="mxchat-actions-grid">
3818 + <div class="mxchat-cards-container">
3819 + <?php if (!empty($actions)) : ?>
3820 + <?php foreach ($actions as $action) :
3821 + $callback_function = $action->callback_function;
3822 + $callback_label = isset($available_callbacks[$callback_function]['label'])
3823 + ? $available_callbacks[$callback_function]['label']
3824 + : $callback_function;
3825 + $threshold_value = isset($action->similarity_threshold)
3826 + ? round($action->similarity_threshold * 100)
3827 + : 85;
3828 +
3829 + // Check if this is a form action
3830 + $is_form_action = strpos($action->intent_label, 'Form ') === 0;
3831 +
3832 + // Get action status (enabled/disabled) - default to true if column doesn't exist
3833 + $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
3834 + ?>
3835 + <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
3836 + <div class="mxchat-card-header">
3837 + <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
3838 + <div class="mxchat-card-toggle">
3839 + <label class="mxchat-switch">
3840 + <input type="checkbox" class="mxchat-action-toggle"
3841 + data-action-id="<?php echo esc_attr($action->id); ?>"
3842 + <?php checked($is_enabled); ?>>
3843 + <span class="mxchat-slider round"></span>
3844 + </label>
3845 + </div>
2651 3846 </div>
2652 - </form>
2653 - </div>
2654 - </div>
2655 -
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;
3847 +
3848 + <div class="mxchat-card-body">
3849 + <div class="mxchat-card-description">
3850 + <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
3851 + <?php echo esc_html($callback_label); ?>
3852 + </div>
3853 +
3854 + <div class="mxchat-card-phrases">
3855 + <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
3856 + <div class="mxchat-phrases-preview">
3857 + <?php
3858 + // Check if the helper function exists, otherwise use a simple substring
3859 + if (method_exists($this, 'get_trimmed_phrases')) {
3860 + echo esc_html($this->get_trimmed_phrases($action->phrases));
3861 + } else {
3862 + echo esc_html(strlen($action->phrases) > 100 ?
3863 + substr($action->phrases, 0, 97) . '...' :
3864 + $action->phrases);
3865 + }
2680 3866 ?>
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>';
2701 - } else {
2702 - echo esc_html($intent->intent_label);
2703 - }
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>
3867 + </div>
3868 + </div>
3869 +
3870 + <div class="mxchat-threshold-control">
3871 + <div class="mxchat-threshold-label">
3872 + <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
3873 + <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
3874 + </div>
3875 + </div>
3876 + </div>
3877 +
3878 + <div class="mxchat-card-footer">
3879 + <?php if (!$is_form_action) : ?>
3880 + <button type="button"
3881 + class="mxchat-button-secondary mxchat-edit-button"
3882 + data-action-id="<?php echo esc_attr($action->id); ?>"
3883 + data-phrases="<?php echo esc_attr($action->phrases); ?>"
3884 + data-label="<?php echo esc_attr($action->intent_label); ?>"
3885 + data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
3886 + data-callback-function="<?php echo esc_attr($action->callback_function); ?>">
3887 + <span class="dashicons dashicons-edit"></span>
3888 + <?php esc_html_e('Edit', 'mxchat'); ?>
3889 + </button>
3890 + <form method="post"
3891 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
3892 + class="mxchat-delete-form"
3893 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
3894 + <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
3895 + <input type="hidden" name="action" value="mxchat_delete_intent">
3896 + <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
3897 + <button type="submit" class="mxchat-button-text mxchat-delete-button">
3898 + <span class="dashicons dashicons-trash"></span>
3899 + <?php esc_html_e('Delete', 'mxchat'); ?>
3900 + </button>
3901 + </form>
3902 + <?php else :
3903 + preg_match('/Form (\d+)/', $action->intent_label, $matches);
3904 + $form_id = isset($matches[1]) ? $matches[1] : '';
3905 + ?>
3906 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
3907 + class="mxchat-button-primary">
3908 + <span class="dashicons dashicons-feedback"></span>
3909 + <?php esc_html_e('Edit Form', 'mxchat'); ?>
3910 + </a>
2780 3911 <?php endif; ?>
2781 - </tbody>
2782 - </table>
2783 - </div>
2784 -
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 - ?>
3912 + </div>
3913 + </div>
3914 + <?php endforeach; ?>
3915 + <?php else : ?>
3916 + <!-- If no actions found -->
3917 + <div class="mxchat-no-actions">
3918 + <div class="mxchat-empty-state">
3919 + <span class="dashicons dashicons-format-chat"></span>
3920 + <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
3921 + <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
3922 + <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
3923 + <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
3924 + </button>
3925 + </div>
2797 3926 </div>
2798 3927 <?php endif; ?>
2799 3928 </div>
2800 3929 </div>
2801 -
2802 - <!-- Edit Modal -->
2803 -<div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
3930 +
3931 + <?php if ($total_pages > 1) : ?>
3932 + <div class="mxchat-pagination">
3933 + <?php
3934 + echo paginate_links(array(
3935 + 'base' => add_query_arg('paged', '%#%'),
3936 + 'format' => '',
3937 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
3938 + 'next_text' => __('Next &raquo;', 'mxchat'),
3939 + 'total' => $total_pages,
3940 + 'current' => $page
3941 + ));
3942 + ?>
3943 + </div>
3944 + <?php endif; ?>
3945 +
3946 +<!-- Add/Edit Action Modal with Step-Based Approach -->
3947 +<!-- Complete Modal HTML with Defined Groups Variable -->
3948 +<div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
2804 3949 <div class="mxchat-modal-content">
2805 3950 <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 3951
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">
3952 + <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3953 + <!-- Dynamic nonce field -->
3954 + <div id="action-nonce-container">
3955 + <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
3956 + </div>
3957 + <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
3958 + <input type="hidden" name="intent_id" id="edit_action_id" value="">
3959 + <input type="hidden" name="callback_function" id="callback_function" value="">
2815 3960
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>
3961 + <!-- Step 1: Action Type Selection -->
3962 + <div id="mxchat-action-step-1" class="mxchat-action-step active">
3963 + <div class="mxchat-step-indicator">
3964 + <div class="mxchat-step-number">1</div>
3965 + <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
3966 + </div>
3967 +
3968 + <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
3969 + <div class="mxchat-action-type-search">
3970 + <span class="dashicons dashicons-search"></span>
3971 + <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
3972 + </div>
3973 +
3974 + <?php
3975 + // Get the callbacks - IMPORTANT: Define the $groups variable here
3976 + $groups = $this->mxchat_get_available_callbacks(true, true);
3977 + ?>
3978 +
3979 + <div class="mxchat-action-type-categories">
3980 + <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
3981 + <?php
3982 + // Get unique categories from the defined groups
3983 + foreach ($groups as $group_label => $group_callbacks) :
3984 + $category_slug = sanitize_title($group_label);
3985 + ?>
3986 + <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
3987 + <?php endforeach; ?>
3988 + </div>
3989 +
3990 + <div class="mxchat-action-types-grid">
3991 + <?php
3992 + // Generate action cards from available callbacks
3993 + foreach ($groups as $group_label => $group_callbacks) :
3994 + $category_slug = sanitize_title($group_label);
3995 +
3996 + foreach ($group_callbacks as $function => $data) :
3997 + $label = $data['label'];
3998 + $pro_only = $data['pro_only'];
3999 + $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
4000 + $description = isset($data['description']) ? $data['description'] : '';
4001 + $is_addon = isset($data['addon']) && $data['addon'] !== false;
4002 + $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
4003 + $is_installed = isset($data['installed']) ? $data['installed'] : true;
4004 +
4005 + // Determine card status and styling
4006 + $card_class = 'mxchat-action-type-card';
4007 + $icon_class = 'mxchat-action-type-icon';
4008 + $status_badge = '';
4009 +
4010 + if ($pro_only && !$this->is_activated) {
4011 + // Pro feature but no Pro license
4012 + $icon_class .= ' pro-feature';
4013 + $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
4014 + }
4015 +
4016 + if ($is_addon && !$is_installed) {
4017 + // Add-on not installed
4018 + $card_class .= ' not-installed';
4019 + $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
4020 + }
4021 +
4022 + // Default description if none provided
4023 + if (empty($description)) {
4024 + $description = sprintf(
4025 + esc_html__('Use the %s action in your chatbot', 'mxchat'),
4026 + $label
4027 + );
4028 + }
4029 + ?>
4030 + <div class="<?php echo esc_attr($card_class); ?>"
4031 + data-category="<?php echo esc_attr($category_slug); ?>"
4032 + data-value="<?php echo esc_attr($function); ?>"
4033 + data-label="<?php echo esc_attr($label); ?>"
4034 + data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
4035 + data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
4036 + data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
4037 + <div class="<?php echo esc_attr($icon_class); ?>">
4038 + <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
4039 + </div>
4040 + <div class="mxchat-action-type-info">
4041 + <h4><?php echo esc_html($label); ?></h4>
4042 + <p><?php echo esc_html($description); ?></p>
4043 + <?php if (!empty($status_badge)) : ?>
4044 + <?php echo $status_badge; ?>
4045 + <?php endif; ?>
4046 +
4047 + <?php if ($is_addon && !$is_installed) : ?>
4048 + <div class="mxchat-addon-info">
4049 + <?php echo esc_html(sprintf(
4050 + __('Requires %s', 'mxchat'),
4051 + $addon_name
4052 + )); ?>
4053 + </div>
4054 + <?php endif; ?>
4055 + </div>
4056 + </div>
4057 + <?php endforeach;
4058 + endforeach; ?>
4059 + </div>
4060 + </div>
4061 +
4062 + <div class="mxchat-modal-actions">
4063 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
4064 + <?php esc_html_e('Cancel', 'mxchat'); ?>
4065 + </button>
4066 + </div>
2825 4067 </div>
2826 4068
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>
4069 + <!-- Step 2: Action Configuration -->
4070 + <div id="mxchat-action-step-2" class="mxchat-action-step">
4071 + <div class="mxchat-step-indicator">
4072 + <div class="mxchat-step-number">2</div>
4073 + <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
4074 + </div>
4075 +
4076 + <div class="mxchat-selected-action">
4077 + <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
4078 + <span class="dashicons dashicons-arrow-left-alt"></span>
4079 + <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
4080 + </button>
4081 + <div class="mxchat-selected-action-info">
4082 + <div id="selected-action-icon" class="mxchat-action-type-icon">
4083 + <span class="dashicons dashicons-admin-generic"></span>
4084 + </div>
4085 + <div class="mxchat-selected-action-details">
4086 + <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
4087 + <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
4088 + </div>
4089 + </div>
4090 + </div>
4091 +
4092 + <div class="mxchat-form-group">
4093 + <label for="intent_label">
4094 + <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
4095 + </label>
4096 + <input name="intent_label" type="text" id="intent_label" required
4097 + class="mxchat-intent-input"
4098 + placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
4099 + </div>
4100 +
4101 + <div class="mxchat-form-group">
4102 + <label for="phrases">
4103 + <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
4104 + </label>
4105 + <textarea name="phrases" id="action_phrases" rows="5" required
4106 + class="mxchat-intent-textarea"
4107 + placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
4108 + </div>
4109 +
4110 + <div class="mxchat-form-group">
4111 + <label for="similarity_threshold">
4112 + <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
4113 + <span class="mxchat-threshold-value-display">85%</span>
4114 + </label>
4115 + <div class="mxchat-slider-group modal-slider">
4116 + <input type="range"
4117 + name="similarity_threshold"
4118 + id="similarity_threshold"
4119 + min="70"
4120 + max="95"
4121 + value="85"
4122 + class="mxchat-intent-slider"
4123 + oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
4124 + </div>
4125 + <div class="mxchat-threshold-hint">
4126 + <?php esc_html_e('Lower values make the action trigger more easily. Higher values require more exact matches.', 'mxchat'); ?>
4127 + </div>
4128 + </div>
4129 +
4130 + <div class="mxchat-modal-actions">
4131 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
4132 + <?php esc_html_e('Cancel', 'mxchat'); ?>
4133 + </button>
4134 + <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
4135 + <?php esc_html_e('Save Action', 'mxchat'); ?>
4136 + </button>
4137 + </div>
2834 4138 </div>
2835 4139 </form>
2836 4140 </div>
2837 4141 </div>
2838 4142
2839 - </div><!-- .mxchat-content -->
4143 +<div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
4144 + <div class="mxchat-action-loading-spinner"></div>
4145 + <div class="mxchat-action-loading-text">
4146 + <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
4147 + </div>
4148 +</div>
2840 4149 </div><!-- .mxchat-wrapper -->
4150 + <?php
4151 +}
2841 4152
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
4153 +/**
4154 + * Helper method to trim phrases for display
4155 + * Adding this in case it doesn't exist in your class
4156 + */
4157 +private function get_trimmed_phrases($phrases, $max_length = 100) {
4158 + if (strlen($phrases) <= $max_length) {
4159 + return $phrases;
4160 + }
4161 +
4162 + $trimmed = substr($phrases, 0, $max_length);
4163 + $last_comma = strrpos($trimmed, ',');
4164 +
4165 + if ($last_comma !== false) {
4166 + $trimmed = substr($trimmed, 0, $last_comma);
4167 + }
4168 +
4169 + return $trimmed . '...';
2849 4170 }
2850 4171
4172 +/**
4173 + * AJAX handler for toggling an action on/off
4174 + */
4175 +public function mxchat_toggle_action() {
4176 + // Check nonce
4177 + if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
4178 + wp_send_json_error(array('message' => 'Security check failed'));
4179 + return;
4180 + }
4181 +
4182 + // Check permissions
4183 + if (!current_user_can('manage_options')) {
4184 + wp_send_json_error(array('message' => 'Permission denied'));
4185 + return;
4186 + }
4187 +
4188 + // Validate params
4189 + $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4190 + $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;
4191 +
4192 + if (!$intent_id) {
4193 + wp_send_json_error(array('message' => 'Invalid action ID'));
4194 + return;
4195 + }
4196 +
4197 + // Update the intent/action status in the database
4198 + global $wpdb;
4199 + $table_name = $wpdb->prefix . 'mxchat_intents';
4200 +
4201 + // Using the 'enabled' field - add this field if it doesn't exist
4202 + $result = $wpdb->update(
4203 + $table_name,
4204 + array('enabled' => $enabled ? 1 : 0),
4205 + array('id' => $intent_id),
4206 + array('%d'),
4207 + array('%d')
4208 + );
4209 +
4210 + if ($result === false) {
4211 + wp_send_json_error(array('message' => 'Database error'));
4212 + return;
4213 + }
4214 +
4215 + wp_send_json_success();
4216 +}
2851 4217
4218 +/**
4219 + * Add the 'enabled' column to the intents table if it doesn't exist
4220 + * Call this during plugin activation or update
4221 + */
4222 +public function mxchat_add_enabled_column_to_intents() {
4223 + global $wpdb;
4224 + $table_name = $wpdb->prefix . 'mxchat_intents';
4225 +
4226 + // Check if the column already exists
4227 + $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
4228 +
4229 + if (empty($columns)) {
4230 + // Add the column with default value of 1 (enabled)
4231 + $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
4232 + }
4233 +}
2852 4234
2853 4235 /**
2854 - * Handle editing of intent phrases
4236 + * Handle embedding generation errors using existing admin notice system
4237 + *
4238 + * @param string $message Error message to display
4239 + * @param bool $redirect Whether to redirect back to the actions page
4240 + * @return void
4241 + */
4242 +private function handle_embedding_error($message, $redirect = true) {
4243 + // Store the error message in the existing transient
4244 + set_transient('mxchat_admin_notice_error', $message, 60);
4245 +
4246 + if ($redirect) {
4247 + // Redirect back to the actions page
4248 + $redirect_url = add_query_arg(
4249 + array(
4250 + 'page' => 'mxchat-actions'
4251 + ),
4252 + admin_url('admin.php')
4253 + );
4254 + wp_safe_redirect($redirect_url);
4255 + exit;
4256 + }
4257 +}
4258 +
4259 +/**
4260 + * Handle editing of intent phrases - with improved error handling
2855 4261 *
2856 4262 * @since 1.0.0
2857 4263 * @return void
2858 4264 */
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'));
2863 - }
2864 -
4265 +public function mxchat_handle_edit_intent() {
4266 + // Security checks (nonce and permissions)
2865 4267 if (!current_user_can('manage_options')) {
2866 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
4268 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2867 4269 }
4270 + check_admin_referer('mxchat_edit_intent');
2868 4271
2869 - // Validate and sanitize input
4272 + // Get POST data
2870 4273 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2871 - if (!$intent_id) {
2872 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
2873 - }
4274 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
4275 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
4276 + $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
4277 + $similarity_threshold = min(95, max(70, $threshold_percentage)) / 100; // Convert to 0.70–0.95
2874 4278
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'));
4279 + // Validate inputs
4280 + if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
4281 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
4282 + return;
2878 4283 }
2879 4284
2880 - // Process phrases the same way as in intent creation
2881 4285 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2882 -
2883 4286 if (empty($phrases_array)) {
2884 - wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
4287 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
4288 + return;
2885 4289 }
2886 4290
2887 - // Generate embeddings and combine them
4291 + // Generate embeddings with improved error handling
2888 4292 $vectors = [];
4293 + $failed_phrases = [];
4294 +
2889 4295 foreach ($phrases_array as $phrase) {
2890 4296 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2891 4297 if (is_array($embedding_vector)) {
2892 4298 $vectors[] = $embedding_vector;
2893 4299 } else {
2894 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
4300 + $failed_phrases[] = $phrase;
2895 4301 }
2896 4302 }
2897 -
4303 +
4304 + if (!empty($failed_phrases)) {
4305 + $this->handle_embedding_error(
4306 + sprintf(
4307 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
4308 + implode(', ', $failed_phrases)
4309 + )
4310 + );
4311 + return;
4312 + }
4313 +
2898 4314 if (empty($vectors)) {
2899 - wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
4315 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
4316 + return;
2900 4317 }
2901 4318
2902 - // Create combined vector just like in intent creation
2903 4319 $combined_vector = $this->mxchat_average_vectors($vectors);
2904 4320 $serialized_vector = maybe_serialize($combined_vector);
2905 4321
4322 + // Update the database
2906 4323 global $wpdb;
2907 4324 $table_name = $wpdb->prefix . 'mxchat_intents';
2908 4325
2909 - // Update the intent with both new phrases and the combined vector
2910 4326 $result = $wpdb->update(
2911 4327 $table_name,
2912 4328 array(
4329 + 'intent_label' => $intent_label,
2913 4330 'phrases' => implode(', ', $phrases_array),
2914 - 'embedding_vector' => $serialized_vector
4331 + 'embedding_vector' => $serialized_vector,
4332 + 'similarity_threshold' => $similarity_threshold
2915 4333 ),
2916 4334 array('id' => $intent_id),
2917 - array('%s', '%s'),
2918 - array('%d')
4335 + array('%s', '%s', '%s', '%f'), // Format: string, string, string, float
4336 + array('%d') // Where format: integer
2919 4337 );
2920 4338
2921 4339 if (false === $result) {
2922 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
4340 + $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
4341 + return;
2923 4342 }
2924 4343
2925 - // Redirect back to the intents page with a success message
4344 + // Set success message and redirect
4345 + set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
4346 +
2926 4347 $redirect_url = add_query_arg(
2927 4348 array(
2928 - 'page' => 'mxchat-intents',
2929 - 'updated' => 'true'
4349 + 'page' => 'mxchat-actions'
2930 4350 ),
2931 4351 admin_url('admin.php')
2932 4352 );
2933 -
2934 4353 wp_safe_redirect($redirect_url);
2935 4354 exit;
2936 4355 }
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 4356
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 -
4357 +/**
4358 + * Handle adding new intent - with improved error handling
4359 + *
4360 + * @return void
4361 + */
2964 4362 public function mxchat_handle_add_intent() {
2965 - if ( ! current_user_can( 'manage_options' ) ) {
2966 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
4363 + if (!current_user_can('manage_options')) {
4364 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2967 4365 }
2968 4366
2969 4367 check_admin_referer('mxchat_add_intent_nonce');
2970 4368
@@ -2976,148 +4374,448 @@
2976 4374 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2977 4375 $default_threshold = 0.85;
2978 4376
2979 4377 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') );
4378 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
4379 + return;
2981 4380 }
2982 4381
2983 4382 $available_callbacks = $this->mxchat_get_available_callbacks();
2984 4383
2985 4384 if (!array_key_exists($callback_function, $available_callbacks)) {
2986 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
4385 + $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
4386 + return;
2987 4387 }
2988 4388
2989 4389 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2990 4390 if ($is_pro_only && !$this->is_activated) {
2991 - wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
4391 + $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
4392 + return;
2992 4393 }
2993 4394
2994 4395 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2995 4396
2996 4397 if (empty($phrases_array)) {
2997 - wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
4398 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
4399 + return;
2998 4400 }
2999 4401
4402 + // Generate embeddings with improved error handling
3000 4403 $vectors = [];
4404 + $failed_phrases = [];
4405 +
3001 4406 foreach ($phrases_array as $phrase) {
3002 4407 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3003 4408 if (is_array($embedding_vector)) {
3004 4409 $vectors[] = $embedding_vector;
3005 4410 } else {
3006 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
4411 + $failed_phrases[] = $phrase;
3007 4412 }
3008 4413 }
4414 +
4415 + if (!empty($failed_phrases)) {
4416 + $this->handle_embedding_error(
4417 + sprintf(
4418 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
4419 + implode(', ', $failed_phrases)
4420 + )
4421 + );
4422 + return;
4423 + }
3009 4424
3010 - if (!empty($vectors)) {
3011 - $combined_vector = $this->mxchat_average_vectors($vectors);
3012 - $serialized_vector = maybe_serialize($combined_vector);
4425 + if (empty($vectors)) {
4426 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
4427 + return;
4428 + }
3013 4429
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 - ]);
4430 + $combined_vector = $this->mxchat_average_vectors($vectors);
4431 + $serialized_vector = maybe_serialize($combined_vector);
3021 4432
3022 - if ($result === false) {
3023 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3024 - }
3025 - } else {
3026 - wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
4433 + $result = $wpdb->insert($table_name, [
4434 + 'intent_label' => $intent_label,
4435 + 'phrases' => implode(', ', $phrases_array),
4436 + 'embedding_vector' => $serialized_vector,
4437 + 'callback_function' => $callback_function,
4438 + 'similarity_threshold' => $default_threshold,
4439 + ]);
4440 +
4441 + if ($result === false) {
4442 + $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
4443 + return;
3027 4444 }
3028 4445
3029 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
4446 + // Set success message
4447 + set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
4448 +
4449 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3030 4450 exit;
3031 4451 }
3032 4452
3033 4453
4454 +/**
4455 + * Update intent threshold with AJAX support
4456 + */
4457 +public function mxchat_update_intent_threshold() {
4458 + // Check permissions
4459 + if (!current_user_can('manage_options')) {
4460 + if (wp_doing_ajax()) {
4461 + wp_send_json_error(array('message' => 'Unauthorized user'));
4462 + return;
4463 + }
4464 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
4465 + }
4466 +
4467 + // Verify nonce
4468 + check_admin_referer('mxchat_update_intent_threshold_nonce');
4469 +
4470 + // Process the update if we have valid data
4471 + if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
4472 + global $wpdb;
4473 + $table_name = $wpdb->prefix . 'mxchat_intents';
4474 + $intent_id = intval($_POST['intent_id']);
4475 + $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
4476 + $similarity_threshold = $threshold_percentage / 100;
4477 +
4478 + $result = $wpdb->update(
4479 + $table_name,
4480 + ['similarity_threshold' => $similarity_threshold],
4481 + ['id' => $intent_id],
4482 + ['%f'],
4483 + ['%d']
4484 + );
4485 +
4486 + // Handle AJAX requests
4487 + if (wp_doing_ajax()) {
4488 + if ($result === false) {
4489 + wp_send_json_error(array('message' => 'Failed to update threshold'));
4490 + } else {
4491 + wp_send_json_success(array('threshold' => $threshold_percentage));
4492 + }
4493 + return;
4494 + }
4495 + }
4496 +
4497 + // Redirect for regular form submissions
4498 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
4499 + exit;
4500 +}
3034 4501
3035 4502
3036 4503
3037 -
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 -
4504 +/**
4505 + * Enhanced get_available_callbacks function with form action exclusion
4506 + *
4507 + * @param bool $grouped Whether to return callbacks grouped by category
4508 + * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
4509 + * @return array Callbacks data with icons, descriptions and availability status
4510 + */
4511 +private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
4512 + // Load WordPress plugin functions if needed
4513 + if (!function_exists('get_plugins')) {
4514 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
4515 + }
4516 +
4517 + // Get active plugins
4518 + $active_plugins = get_option('active_plugins', array());
4519 +
4520 + // Functions to exclude from the action selector
4521 + $excluded_functions = array(
4522 + 'mxchat_handle_form_collection' // Exclude form actions
4523 + );
4524 +
4525 + // Define add-on plugin files and their corresponding action functions
4526 + $addon_plugins = array(
4527 + 'mxchat-woo/mxchat-woo.php' => array(
4528 + 'functions' => array(
4529 + 'mxchat_handle_product_recommendations',
4530 + 'mxchat_handle_order_history',
4531 + 'mxchat_show_product_card',
4532 + 'mxchat_add_to_cart',
4533 + 'mxchat_checkout_redirect'
4534 + ),
4535 + 'name' => __('WooCommerce Add-on', 'mxchat'),
4536 + 'pro_required' => true
4537 + ),
4538 + 'mxchat-perplexity/mxchat-perplexity.php' => array(
4539 + 'functions' => array('mxchat_perplexity_research'),
4540 + 'name' => __('Perplexity Add-on', 'mxchat'),
4541 + 'pro_required' => true
4542 + ),
4543 + // Add other add-ons and their functions here
4544 + );
4545 +
4546 + // Get the functions that are provided by active add-ons
4547 + $addon_provided_functions = array();
4548 + $addon_function_mapping = array(); // Maps functions to their add-on info
4549 +
4550 + // Check which add-ons are active
4551 + foreach ($addon_plugins as $plugin_file => $addon_info) {
4552 + $is_active = in_array($plugin_file, $active_plugins);
4553 +
4554 + // For each function in this addon
4555 + foreach ($addon_info['functions'] as $function) {
4556 + // Consider a function installed only if:
4557 + // 1. The add-on is active AND
4558 + // 2. Either it doesn't require Pro OR Pro is activated
4559 + $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
4560 +
4561 + // If the add-on is installed, mark this function as provided by an add-on
4562 + if ($is_installed) {
4563 + $addon_provided_functions[] = $function;
4564 + }
4565 +
4566 + // Store addon info for this function regardless of installation status
4567 + $addon_function_mapping[$function] = array(
4568 + 'addon' => basename(dirname($plugin_file)),
4569 + 'addon_name' => $addon_info['name'],
4570 + 'pro_required' => $addon_info['pro_required'],
4571 + 'is_active' => $is_active,
4572 + 'is_installed' => $is_installed
4573 + );
4574 + }
4575 + }
4576 +
4577 + // Core callbacks - always available in the base plugin
4578 + $core_callbacks = array(
4579 + 'mxchat_handle_email_capture' => array(
4580 + 'label' => __('Loops Email Capture', 'mxchat'),
4581 + 'pro_only' => false,
4582 + 'group' => __('Customer Engagement', 'mxchat'),
4583 + 'icon' => 'email-alt',
4584 + 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
4585 + 'addon' => false, // Not from an add-on
4586 + 'installed' => true // Always installed with base plugin
4587 + ),
4588 + 'mxchat_handle_search_request' => array(
4589 + 'label' => __('Brave Web Search', 'mxchat'),
4590 + 'pro_only' => false,
4591 + 'group' => __('Search Features', 'mxchat'),
4592 + 'icon' => 'search',
4593 + 'description' => __('Let users search the web directly from the chat', 'mxchat'),
4594 + 'addon' => false,
4595 + 'installed' => true
4596 + ),
4597 + 'mxchat_handle_image_search_request' => array(
4598 + 'label' => __('Brave Image Search', 'mxchat'),
4599 + 'pro_only' => false,
4600 + 'group' => __('Search Features', 'mxchat'),
4601 + 'icon' => 'format-image',
4602 + 'description' => __('Search and display images in the chat conversation', 'mxchat'),
4603 + 'addon' => false,
4604 + 'installed' => true
4605 + ),
4606 + // Pro core features - check is_activated property
4607 + 'mxchat_generate_image' => array(
4608 + 'label' => __('Generate Image', 'mxchat'),
4609 + 'pro_only' => false,
4610 + 'group' => __('Other Features', 'mxchat'),
4611 + 'icon' => 'art',
4612 + 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
4613 + 'addon' => false,
4614 + 'installed' => $this->is_activated
4615 + ),
4616 + 'mxchat_handle_pdf_discussion' => array(
4617 + 'label' => __('Chat with PDF', 'mxchat'),
4618 + 'pro_only' => true,
4619 + 'group' => __('Other Features', 'mxchat'),
4620 + 'icon' => 'media-document',
4621 + 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
4622 + 'addon' => false,
4623 + 'installed' => $this->is_activated
4624 + ),
4625 + 'mxchat_live_agent_handover' => array(
4626 + 'label' => __('Slack Live Agent', 'mxchat'),
4627 + 'pro_only' => true,
4628 + 'group' => __('Customer Engagement', 'mxchat'),
4629 + 'icon' => 'admin-users',
4630 + 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
4631 + 'addon' => false,
4632 + 'installed' => $this->is_activated
4633 + ),
4634 + 'mxchat_handle_switch_to_chatbot_intent' => array(
4635 + 'label' => __('Back to Chatbot', 'mxchat'),
4636 + 'pro_only' => true,
4637 + 'group' => __('Customer Engagement', 'mxchat'),
4638 + 'icon' => 'backup',
4639 + 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
4640 + 'addon' => false,
4641 + 'installed' => $this->is_activated
4642 + ),
4643 + );
4644 +
4645 + // Add-on callbacks with placeholders - only include if the add-on is NOT active
4646 + $addon_callbacks = array(
4647 + // WooCommerce Add-on
4648 + 'mxchat_handle_product_recommendations' => array(
4649 + 'label' => __('Product Recommendations', 'mxchat'),
4650 + 'pro_only' => true,
4651 + 'group' => __('WooCommerce Features', 'mxchat'),
4652 + 'icon' => 'cart',
4653 + 'description' => __('Suggest products based on customer preferences', 'mxchat'),
4654 + ),
4655 + 'mxchat_handle_order_history' => array(
4656 + 'label' => __('Order History', 'mxchat'),
4657 + 'pro_only' => true,
4658 + 'group' => __('WooCommerce Features', 'mxchat'),
4659 + 'icon' => 'clipboard',
4660 + 'description' => __('Allow customers to check their order status', 'mxchat'),
4661 + ),
4662 + 'mxchat_show_product_card' => array(
4663 + 'label' => __('Show Product Card', 'mxchat'),
4664 + 'pro_only' => true,
4665 + 'group' => __('WooCommerce Features', 'mxchat'),
4666 + 'icon' => 'products',
4667 + 'description' => __('Display product information in the chat', 'mxchat'),
4668 + ),
4669 + 'mxchat_add_to_cart' => array(
4670 + 'label' => __('Add to Cart', 'mxchat'),
4671 + 'pro_only' => true,
4672 + 'group' => __('WooCommerce Features', 'mxchat'),
4673 + 'icon' => 'plus-alt',
4674 + 'description' => __('Add products to cart directly from chat', 'mxchat'),
4675 + ),
4676 + 'mxchat_checkout_redirect' => array(
4677 + 'label' => __('Proceed to Checkout', 'mxchat'),
4678 + 'pro_only' => true,
4679 + 'group' => __('WooCommerce Features', 'mxchat'),
4680 + 'icon' => 'arrow-right-alt',
4681 + 'description' => __('Redirect customer to checkout page', 'mxchat'),
4682 + ),
4683 +
4684 + // Perplexity Add-on
4685 + 'mxchat_perplexity_research' => array(
4686 + 'label' => __('Perplexity Research', 'mxchat'),
4687 + 'pro_only' => true,
4688 + 'group' => __('Search Features', 'mxchat'),
4689 + 'icon' => 'book-alt',
4690 + 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
4691 + ),
4692 + );
4693 +
4694 + // Enhance add-on callbacks with installation status and addon info
4695 + foreach ($addon_callbacks as $function => $data) {
4696 + if (isset($addon_function_mapping[$function])) {
4697 + $addon_info = $addon_function_mapping[$function];
4698 +
4699 + $addon_callbacks[$function]['addon'] = $addon_info['addon'];
4700 + $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4701 + $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
4702 +
4703 + // Set pro_only based on add-on configuration
4704 + $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4705 + } else {
4706 + $addon_callbacks[$function]['addon'] = 'unknown';
4707 + $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
4708 + $addon_callbacks[$function]['installed'] = false;
4709 + }
4710 + }
4711 +
4712 + // Initialize callbacks with core features
4713 + $callbacks = $core_callbacks;
4714 +
4715 + // Get callbacks from active add-ons
4716 + $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
4717 +
4718 + // Add placeholder callbacks only for add-ons that aren't active
4719 + if ($include_all) {
4720 + foreach ($addon_callbacks as $function => $data) {
4721 + // Skip placeholders for functions provided by active add-ons
4722 + if (in_array($function, $addon_provided_functions)) {
4723 + continue;
4724 + }
4725 +
4726 + // Skip excluded functions
4727 + if (in_array($function, $excluded_functions)) {
4728 + continue;
4729 + }
4730 +
4731 + // Add the placeholder
4732 + $callbacks[$function] = $data;
4733 + }
4734 + }
4735 +
4736 + // Add callbacks from active add-ons (will override placeholders)
4737 + foreach ($active_addon_callbacks as $function => $data) {
4738 + // Skip excluded functions
4739 + if (in_array($function, $excluded_functions)) {
4740 + continue;
4741 + }
4742 +
4743 + // Always include callbacks from add-ons
4744 + $callbacks[$function] = $data;
4745 +
4746 + // Ensure they have the proper add-on info
4747 + if (isset($addon_function_mapping[$function])) {
4748 + $addon_info = $addon_function_mapping[$function];
4749 + $callbacks[$function]['addon'] = $addon_info['addon'];
4750 + $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4751 + $callbacks[$function]['installed'] = $addon_info['is_installed'];
4752 + $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4753 + }
4754 + }
4755 +
4756 + // Just before returning callbacks, sort them to prioritize free features
4757 + if (!$grouped) {
4758 + // Create temporary arrays for sorting
4759 + $free_callbacks = array();
4760 + $pro_callbacks = array();
4761 +
4762 + // Split callbacks into free and pro
4763 + foreach ($callbacks as $key => $data) {
4764 + if (isset($data['pro_only']) && $data['pro_only']) {
4765 + $pro_callbacks[$key] = $data;
4766 + } else {
4767 + $free_callbacks[$key] = $data;
4768 + }
4769 + }
4770 +
4771 + // Merge with free callbacks first
4772 + $callbacks = array_merge($free_callbacks, $pro_callbacks);
4773 + }
4774 +
3102 4775 // Return grouped structure if requested
3103 4776 if ($grouped) {
3104 - $grouped_callbacks = [];
4777 + $grouped_callbacks = array();
3105 4778 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 - ];
4779 + $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
4780 +
4781 + // Ensure we carry forward all the new fields in grouped mode
4782 + $callback_data = array(
4783 + 'label' => $data['label'],
4784 + 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
4785 + 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
4786 + 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
4787 + 'addon' => isset($data['addon']) ? $data['addon'] : false,
4788 + 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
4789 + 'installed' => isset($data['installed']) ? $data['installed'] : true
4790 + );
4791 +
4792 + $grouped_callbacks[$group_label][$key] = $callback_data;
3111 4793 }
4794 +
4795 + // Sort within each group to prioritize free features
4796 + foreach ($grouped_callbacks as $group => $items) {
4797 + $free_items = array();
4798 + $pro_items = array();
4799 +
4800 + foreach ($items as $key => $data) {
4801 + if (isset($data['pro_only']) && $data['pro_only']) {
4802 + $pro_items[$key] = $data;
4803 + } else {
4804 + $free_items[$key] = $data;
4805 + }
4806 + }
4807 +
4808 + $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
4809 + }
4810 +
3112 4811 return $grouped_callbacks;
3113 4812 }
3114 -
4813 +
3115 4814 return $callbacks;
3116 4815 }
3117 4816
3118 4817
3119 -
3120 4818 private function mxchat_average_vectors($vectors) {
3121 4819 $vector_length = count($vectors[0]);
3122 4820 $sum_vector = array_fill(0, $vector_length, 0);
3123 4821
@@ -3150,9 +4848,9 @@
3150 4848
3151 4849 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3152 4850 }
3153 4851
3154 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
4852 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3155 4853 exit;
3156 4854 }
3157 4855
3158 4856
@@ -3203,41 +4901,98 @@
3203 4901 );
3204 4902
3205 4903
3206 4904 // Existing fields...
3207 - add_settings_field(
3208 - 'api_key',
3209 - esc_html__('OpenAI API Key', 'mxchat'),
3210 - array($this, 'api_key_callback'),
3211 - 'mxchat-chatbot',
3212 - 'mxchat_chatbot_section'
3213 - );
4905 + add_settings_field(
4906 + 'api_key',
4907 + esc_html__('OpenAI API Key', 'mxchat'),
4908 + array($this, 'api_key_callback'),
4909 + 'mxchat-chatbot',
4910 + 'mxchat_chatbot_section',
4911 + array(
4912 + 'class' => 'mxchat-setting-row',
4913 + 'data-provider' => 'openai'
4914 + )
4915 + );
4916 +
4917 + add_settings_field(
4918 + 'xai_api_key',
4919 + esc_html__('X.AI API Key', 'mxchat'),
4920 + array($this, 'xai_api_key_callback'),
4921 + 'mxchat-chatbot',
4922 + 'mxchat_chatbot_section',
4923 + array(
4924 + 'class' => 'mxchat-setting-row',
4925 + 'data-provider' => 'xai'
4926 + )
4927 + );
4928 +
4929 + add_settings_field(
4930 + 'claude_api_key',
4931 + esc_html__('Claude API Key', 'mxchat'),
4932 + array($this, 'claude_api_key_callback'),
4933 + 'mxchat-chatbot',
4934 + 'mxchat_chatbot_section',
4935 + array(
4936 + 'class' => 'mxchat-setting-row',
4937 + 'data-provider' => 'claude'
4938 + )
4939 + );
4940 +
4941 + add_settings_field(
4942 + 'deepseek_api_key',
4943 + esc_html__('DeepSeek API Key', 'mxchat'),
4944 + array($this, 'deepseek_api_key_callback'),
4945 + 'mxchat-chatbot',
4946 + 'mxchat_chatbot_section',
4947 + array(
4948 + 'class' => 'mxchat-setting-row',
4949 + 'data-provider' => 'deepseek'
4950 + )
4951 + );
4952 +
4953 + add_settings_field(
4954 + 'gemini_api_key',
4955 + esc_html__('Google Gemini API Key', 'mxchat'),
4956 + array($this, 'gemini_api_key_callback'),
4957 + 'mxchat-chatbot',
4958 + 'mxchat_chatbot_section',
4959 + array(
4960 + 'class' => 'mxchat-setting-row',
4961 + 'data-provider' => 'gemini'
4962 + )
4963 + );
4964 +
4965 + add_settings_field(
4966 + 'voyage_api_key',
4967 + esc_html__('Voyage AI API Key', 'mxchat'),
4968 + array($this, 'voyage_api_key_callback'),
4969 + 'mxchat-chatbot',
4970 + 'mxchat_chatbot_section',
4971 + array(
4972 + 'class' => 'mxchat-setting-row',
4973 + 'data-provider' => 'voyage'
4974 + )
4975 + );
3214 4976
3215 4977 add_settings_field(
3216 - 'xai_api_key',
3217 - esc_html__('X.AI API Key', 'mxchat'),
3218 - array($this, 'xai_api_key_callback'),
4978 + 'model',
4979 + esc_html__('Chat Model', 'mxchat'),
4980 + array($this, 'mxchat_model_callback'),
3219 4981 'mxchat-chatbot',
3220 4982 'mxchat_chatbot_section'
3221 4983 );
3222 -
4984 +
4985 + // Add the settings field
3223 4986 add_settings_field(
3224 - 'claude_api_key',
3225 - esc_html__('Claude API Key', 'mxchat'),
3226 - array($this, 'claude_api_key_callback'),
4987 + 'embedding_model',
4988 + esc_html__('Embedding Model', 'mxchat'),
4989 + array($this, 'embedding_model_callback'),
3227 4990 'mxchat-chatbot',
3228 4991 'mxchat_chatbot_section'
3229 4992 );
3230 -
4993 +
3231 4994 add_settings_field(
3232 - 'deepseek_api_key',
3233 - esc_html__('DeepSeek API Key', 'mxchat'),
3234 - array($this, 'deepseek_api_key_callback'),
3235 - 'mxchat-chatbot',
3236 - 'mxchat_chatbot_section'
3237 - );
3238 -
3239 - add_settings_field(
3240 4995 'system_prompt_instructions',
3241 4996 esc_html__('AI Instructions (Behavior)', 'mxchat'),
3242 4997 array($this, 'system_prompt_instructions_callback'),
3243 4998 'mxchat-chatbot',
@@ -3243,24 +4998,25 @@
3243 4998 'mxchat-chatbot',
3244 4999 'mxchat_chatbot_section'
3245 5000 );
3246 5001
5002 +
3247 5003 add_settings_field(
3248 - 'model',
3249 - esc_html__('Model', 'mxchat'),
3250 - array($this, 'mxchat_model_callback'),
5004 + 'top_bar_title',
5005 + esc_html__('Top Bar Title', 'mxchat'),
5006 + array($this, 'mxchat_top_bar_title_callback'),
3251 5007 'mxchat-chatbot',
3252 5008 'mxchat_chatbot_section'
3253 5009 );
3254 5010
3255 5011 add_settings_field(
3256 - 'top_bar_title',
3257 - esc_html__('Top Bar Title', 'mxchat'),
3258 - array($this, 'mxchat_top_bar_title_callback'),
5012 + 'ai_agent_text',
5013 + esc_html__('AI Agent Text', 'mxchat'),
5014 + array($this, 'mxchat_ai_agent_text_callback'),
3259 5015 'mxchat-chatbot',
3260 5016 'mxchat_chatbot_section'
3261 5017 );
3262 -
5018 +
3263 5019 add_settings_field(
3264 5020 'enable_email_block',
3265 5021 esc_html__('Require Email To Chat', 'mxchat'),
3266 5022 array($this, 'enable_email_block_callback'),
@@ -3365,9 +5121,9 @@
3365 5121 );
3366 5122
3367 5123 add_settings_field(
3368 5124 'popular_question_1',
3369 - esc_html__('Popular Question 1', 'mxchat'),
5125 + esc_html__('Quick Question 1', 'mxchat'),
3370 5126 array($this, 'mxchat_popular_question_1_callback'),
3371 5127 'mxchat-chatbot',
3372 5128 'mxchat_chatbot_section'
3373 5129 );
@@ -3373,9 +5129,9 @@
3373 5129 );
3374 5130
3375 5131 add_settings_field(
3376 5132 'popular_question_2',
3377 - esc_html__('Popular Question 2', 'mxchat'),
5133 + esc_html__('Quick Question 2', 'mxchat'),
3378 5134 array($this, 'mxchat_popular_question_2_callback'),
3379 5135 'mxchat-chatbot',
3380 5136 'mxchat_chatbot_section'
3381 5137 );
@@ -3381,9 +5137,9 @@
3381 5137 );
3382 5138
3383 5139 add_settings_field(
3384 5140 'popular_question_3',
3385 - esc_html__('Popular Question 3', 'mxchat'),
5141 + esc_html__('Quick Question 3', 'mxchat'),
3386 5142 array($this, 'mxchat_popular_question_3_callback'),
3387 5143 'mxchat-chatbot',
3388 5144 'mxchat_chatbot_section'
3389 5145 );
@@ -3389,21 +5145,14 @@
3389 5145 );
3390 5146
3391 5147 add_settings_field(
3392 5148 'additional_popular_questions',
3393 - esc_html__('Additional Popular Questions', 'mxchat'),
5149 + esc_html__('Additional Quick Questions', 'mxchat'),
3394 5150 array($this, 'mxchat_additional_popular_questions_callback'),
3395 5151 'mxchat-chatbot',
3396 5152 'mxchat_chatbot_section'
3397 5153 );
3398 5154
3399 - // WooCommerce Settings Section
3400 - add_settings_section(
3401 - 'mxchat_woocommerce_section',
3402 - esc_html__('WooCommerce Settings', 'mxchat'),
3403 - null,
3404 - 'mxchat-embed'
3405 - );
3406 5155
3407 5156 // Loops Settings Section
3408 5157 add_settings_section(
3409 5158 'mxchat_loops_section',
@@ -3411,33 +5160,8 @@
3411 5160 null,
3412 5161 'mxchat-embed'
3413 5162 );
3414 5163
3415 - // WooCommerce Settings Fields
3416 - 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 5164 // Loops Settings Fields
3441 5165 add_settings_field(
3442 5166 'loops_api_key',
3443 5167 esc_html__('Loops API Key', 'mxchat'),
@@ -3540,8 +5264,26 @@
3540 5264 array($this, 'mxchat_chat_toolbar_toggle_callback'),
3541 5265 'mxchat-embed',
3542 5266 'mxchat_pdf_intent_section'
3543 5267 );
5268 +
5269 + // PDF Upload Button Toggle
5270 + add_settings_field(
5271 + 'show_pdf_upload_button',
5272 + __('Show PDF Upload Button', 'mxchat'),
5273 + array($this, 'mxchat_show_pdf_upload_button_callback'),
5274 + 'mxchat-embed',
5275 + 'mxchat_pdf_intent_section'
5276 + );
5277 +
5278 + // Word Upload Button Toggle
5279 + add_settings_field(
5280 + 'show_word_upload_button',
5281 + __('Show Word Upload Button', 'mxchat'),
5282 + array($this, 'mxchat_show_word_upload_button_callback'),
5283 + 'mxchat-embed',
5284 + 'mxchat_pdf_intent_section'
5285 + );
3544 5286
3545 5287 add_settings_field(
3546 5288 'pdf_intent_trigger_text',
3547 5289 __('Intent Trigger Text', 'mxchat'),
@@ -3638,9 +5380,9 @@
3638 5380
3639 5381 // General Settings Section
3640 5382 add_settings_section(
3641 5383 'mxchat_general_section',
3642 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
5384 + esc_html__('YouTube Tutorials', 'mxchat'),
3643 5385 null,
3644 5386 'mxchat-general'
3645 5387 );
3646 5388 }
@@ -3883,52 +5625,8 @@
3883 5625 echo '</div>';
3884 5626 }
3885 5627
3886 5628
3887 -public function mxchat_enable_woocommerce_integration_callback() {
3888 - // Load from mxchat_options array
3889 - $options = get_option('mxchat_options', []);
3890 -
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>';
3926 -}
3927 -
3928 -
3929 -
3930 -
3931 5629 private function mxchat_add_option_field($id, $title, $callback = '') {
3932 5630 add_settings_field(
3933 5631 $id,
3934 5632 __($title, 'mxchat'),
@@ -3937,163 +5635,90 @@
3937 5635 'mxchat_setting_section_id',
3938 5636 $id === 'model' ? ['label_for' => 'model'] : []
3939 5637 );
3940 5638 }
3941 -
5639 +
5640 +// OpenAI API Key
3942 5641 public function api_key_callback() {
3943 - // Retrieve from your stored 'api_key' in the mxchat_options array
3944 5642 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3945 -
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" />';
5643 +
5644 + echo '<div class="api-key-wrapper" data-provider="openai">';
5645 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
3948 5646 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>';
5647 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
5648 + echo '</div>';
3950 5649 }
5650 +
5651 +// X.AI API Key
3951 5652 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 5653 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3958 -
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 - );
5654 +
5655 + echo '<div class="api-key-wrapper" data-provider="xai">';
5656 + echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
3966 5657 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 -
5658 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3975 5659 echo '</div>';
3976 5660 }
5661 +// Claude API Key
3977 5662 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 5663 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3984 5664
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 - );
5665 + echo '<div class="api-key-wrapper" data-provider="claude">';
5666 + echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
3992 5667 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 - }
5668 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3999 5669 echo '</div>';
4000 5670 }
5671 +
5672 +// DeepSeek API Key
4001 5673 public function deepseek_api_key_callback() {
4002 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
4003 5674 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4004 -
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" />';
5675 +
5676 + echo '<div class="api-key-wrapper" data-provider="deepseek">';
5677 + echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4007 5678 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5679 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
5680 + echo '</div>';
4008 5681 }
4009 5682
4010 -
4011 -public function mxchat_woocommerce_consumer_key_callback() {
4012 - // Load the entire 'mxchat_options' array
4013 - $all_options = get_option('mxchat_options', []);
4014 -
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 -
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 -
5683 +// Gemini API Key
5684 +public function gemini_api_key_callback() {
5685 + $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
5686 +
5687 + echo '<div class="api-key-wrapper" data-provider="gemini">';
5688 + echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
5689 + echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5690 + echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
4041 5691 echo '</div>';
4042 5692 }
4043 5693
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 - : '';
4049 -
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 -
5694 +// Voyage API Key
5695 +public function voyage_api_key_callback() {
5696 + $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
5697 +
5698 + echo '<div class="api-key-wrapper" data-provider="voyage">';
5699 + echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5700 + echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5701 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
4080 5702 echo '</div>';
4081 5703 }
5704 +
4082 5705 public function mxchat_loops_api_key_callback() {
4083 - // Support both old and new format
4084 5706 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4085 -
4086 - echo '<div class="api-key-wrapper">';
5707 +
5708 + // Hidden fields to "trap" autofill
5709 + echo '<input type="text" style="display:none" autocomplete="username" />';
5710 + echo '<input type="password" style="display:none" autocomplete="current-password" />';
5711 +
5712 + echo '<div class="api-key-wrapper" data-provider="loops">';
4087 5713 echo sprintf(
4088 - '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
5714 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
4089 5715 $loops_api_key
4090 5716 );
4091 5717 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4092 5718 echo '</div>';
4093 - echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5719 + echo '<p class="description">' . esc_html__('Enter your Loops API Key here. Once entered, refreshed page to load list (See FAQ for details)', 'mxchat') . '</p>';
4094 5720 }
4095 -
4096 5721 public function mxchat_loops_mailing_list_callback() {
4097 5722 // Add error handling and type checking
4098 5723 $loops_api_key = '';
4099 5724 $selected_list = '';
@@ -4129,8 +5754,9 @@
4129 5754 } else {
4130 5755 echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
4131 5756 }
4132 5757 }
5758 +
4133 5759 public function mxchat_triggered_phrase_response_callback() {
4134 5760 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4135 5761 $triggered_response = isset($this->options['triggered_phrase_response'])
4136 5762 ? $this->options['triggered_phrase_response']
@@ -4192,10 +5818,19 @@
4192 5818
4193 5819 public function mxchat_model_callback() {
4194 5820 // Define available models grouped by provider
4195 5821 $models = array(
5822 + esc_html__('Google Gemini Models', 'mxchat') => array(
5823 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5824 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5825 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5826 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5827 + ),
4196 5828 esc_html__('X.AI Models', 'mxchat') => array(
4197 - 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
5829 + 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
5830 + 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
5831 + 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
5832 + 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
4198 5833 'grok-2' => esc_html__('Grok 2', 'mxchat')
4199 5834 ),
4200 5835 esc_html__('DeepSeek Models', 'mxchat') => array(
4201 5836 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
@@ -4200,14 +5835,16 @@
4200 5835 esc_html__('DeepSeek Models', 'mxchat') => array(
4201 5836 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4202 5837 ),
4203 5838 esc_html__('Claude Models', 'mxchat') => array(
4204 - 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
5839 + 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (Most Intelligent)', 'mxchat'),
5840 + 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Intelligent)', 'mxchat'),
4205 5841 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
4206 5842 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4207 5843 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4208 5844 ),
4209 5845 esc_html__('OpenAI Models', 'mxchat') => array(
5846 + 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4210 5847 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4211 5848 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4212 5849 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4213 5850 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
@@ -4213,40 +5850,64 @@
4213 5850 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4214 5851 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4215 5852 )
4216 5853 );
4217 -
5854 +
4218 5855 // Retrieve the currently selected model from saved options
4219 5856 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4220 -
5857 +
4221 5858 // Begin the select dropdown
4222 - echo '<select id="model" name="model">'; // No array notation in name attribute
4223 -
5859 + echo '<select id="model" name="model">';
5860 +
4224 5861 // Iterate over groups of models
4225 5862 foreach ($models as $group_label => $group_models) {
4226 5863 echo '<optgroup label="' . esc_attr($group_label) . '">';
4227 -
5864 +
4228 5865 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>';
5866 + // All models enabled - no disabled attribute or Pro Only label
5867 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4235 5868 }
4236 -
5869 +
4237 5870 echo '</optgroup>';
4238 5871 }
4239 -
5872 +
4240 5873 // Close the select dropdown
4241 5874 echo '</select>';
5875 +
5876 + // Updated description to remove mention of Pro-only models
5877 + echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
5878 +}
4242 5879
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>';
5880 +
5881 +// Callback function for embedding model selection
5882 +public function embedding_model_callback() {
5883 + $models = array(
5884 + esc_html__('OpenAI Embeddings', 'mxchat') => array(
5885 + 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
5886 + 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
5887 + 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
5888 + ),
5889 + esc_html__('Voyage AI Embeddings', 'mxchat') => array(
5890 + 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
5891 + )
5892 + );
5893 +
5894 + $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
5895 +
5896 + echo '<select id="embedding_model" name="embedding_model">';
5897 + foreach ($models as $group_label => $group_models) {
5898 + echo '<optgroup label="' . esc_attr($group_label) . '">';
5899 + foreach ($group_models as $model_value => $model_label) {
5900 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
5901 + }
5902 + echo '</optgroup>';
5903 + }
5904 + echo '</select>';
5905 +echo '<p class="description"><span class="red-warning">IMPORTANT:</span> Select the model for vector embeddings. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
5906 +
4245 5907 }
4246 5908
4247 5909
4248 -
4249 5910 public function mxchat_top_bar_title_callback() {
4250 5911 // Retrieve the current value of the top bar title from saved options
4251 5912 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4252 5913
@@ -4255,9 +5916,16 @@
4255 5916
4256 5917 // Add a description
4257 5918 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4258 5919 }
4259 -
5920 +public function mxchat_ai_agent_text_callback() {
5921 + // Retrieve the current value of the AI agent text from saved options
5922 + $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
5923 + // Render the input field
5924 + echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
5925 + // Add a description
5926 + echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
5927 +}
4260 5928 public function enable_email_block_callback() {
4261 5929 // Load full plugin options array
4262 5930 $all_options = get_option('mxchat_options', []);
4263 5931
@@ -4277,9 +5945,8 @@
4277 5945 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 5946 }
4279 5947
4280 5948
4281 -
4282 5949 public function email_blocker_header_content_callback() {
4283 5950 // Load the entire 'mxchat_options' array
4284 5951 $all_options = get_option('mxchat_options', []);
4285 5952
@@ -4287,14 +5954,15 @@
4287 5954 $content = isset($all_options['email_blocker_header_content'])
4288 5955 ? $all_options['email_blocker_header_content']
4289 5956 : '';
4290 5957
4291 - // Render the textarea
5958 + // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4292 5959 echo '<textarea
4293 5960 id="email_blocker_header_content"
4294 5961 name="email_blocker_header_content"
4295 5962 rows="5"
4296 5963 cols="70"
5964 + data-setting="email_blocker_header_content"
4297 5965 >' . esc_textarea($content) . '</textarea>';
4298 5966
4299 5967 echo '<p class="description">';
4300 5968 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 +5968,8 @@
4300 5968 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 5969 echo '</p>';
4302 5970 }
4303 5971
4304 -
4305 5972 public function email_blocker_button_text_callback() {
4306 5973 // Load the entire 'mxchat_options' array
4307 5974 $all_options = get_option('mxchat_options', []);
4308 5975
@@ -4323,23 +5990,20 @@
4323 5990
4324 5991 public function mxchat_intro_message_callback() {
4325 5992 // Load the entire 'mxchat_options' array
4326 5993 $all_options = get_option('mxchat_options', []);
4327 -
4328 5994 // Retrieve the saved intro message or use the default
4329 5995 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4330 5996 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4331 -
4332 - // Output the textarea with the saved value
5997 + // Output the textarea with the saved value without escaping HTML
4333 5998 ?>
4334 - <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
5999 + <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4335 6000 <p class="description">
4336 - <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
6001 + <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4337 6002 </p>
4338 6003 <?php
4339 6004 }
4340 6005
4341 -
4342 6006 public function mxchat_input_copy_callback() {
4343 6007 // Load the entire 'mxchat_options' array
4344 6008 $all_options = get_option('mxchat_options', []);
4345 6009
@@ -4909,13 +6573,13 @@
4909 6573 // Render the input field
4910 6574 printf(
4911 6575 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4912 6576 esc_attr($popular_question_1),
4913 - esc_attr__('Enter Popular Question 1', 'mxchat')
6577 + esc_attr__('Enter Quick Question 1', 'mxchat')
4914 6578 );
4915 6579
4916 6580 // 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>';
6581 + echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
4918 6582 }
4919 6583
4920 6584
4921 6585 public function mxchat_popular_question_2_callback() {
@@ -4928,13 +6592,13 @@
4928 6592 // Render the input field
4929 6593 printf(
4930 6594 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4931 6595 esc_attr($popular_question_2),
4932 - esc_attr__('Enter Popular Question 2', 'mxchat')
6596 + esc_attr__('Enter Quick Question 2', 'mxchat')
4933 6597 );
4934 6598
4935 6599 // 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>';
6600 + echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
4937 6601 }
4938 6602
4939 6603
4940 6604 public function mxchat_popular_question_3_callback() {
@@ -4947,13 +6611,13 @@
4947 6611 // Render the input field
4948 6612 printf(
4949 6613 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
4950 6614 esc_attr($popular_question_3),
4951 - esc_attr(__('Enter Popular Question 3', 'mxchat'))
6615 + esc_attr(__('Enter Quick Question 3', 'mxchat'))
4952 6616 );
4953 6617
4954 6618 // 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>';
6619 + echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
4956 6620 }
4957 6621
4958 6622 public function mxchat_additional_popular_questions_callback() {
4959 6623 $options = get_option('mxchat_options', []);
@@ -4974,9 +6638,9 @@
4974 6638 <button type="button" class="button mxchat-remove-question"
4975 6639 aria-label="%s">%s</button>
4976 6640 </div>',
4977 6641 esc_attr($question),
4978 - esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
6642 + esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
4979 6643 $index,
4980 6644 esc_attr(__('Remove question', 'mxchat')),
4981 6645 esc_html__('Remove', 'mxchat')
4982 6646 );
@@ -4991,9 +6655,9 @@
4991 6655 data-question-index="0" />
4992 6656 <button type="button" class="button mxchat-remove-question"
4993 6657 aria-label="%s">%s</button>
4994 6658 </div>',
4995 - esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
6659 + esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
4996 6660 esc_attr(__('Remove question', 'mxchat')),
4997 6661 esc_html__('Remove', 'mxchat')
4998 6662 );
4999 6663 }
@@ -5002,9 +6666,9 @@
5002 6666 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5003 6667 esc_attr(__('Add question', 'mxchat')),
5004 6668 esc_html__('Add Question', 'mxchat')
5005 6669 );
5006 - echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
6670 + echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5007 6671 echo '</div>';
5008 6672 }
5009 6673
5010 6674 public function mxchat_brave_api_key_callback() {
@@ -5136,9 +6800,78 @@
5136 6800 echo '</div>';
5137 6801 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>';
5138 6802 }
5139 6803
6804 +/**
6805 + * Callback for PDF upload button toggle setting
6806 + */
6807 +public function mxchat_show_pdf_upload_button_callback() {
6808 + // Get toggle value with fallback
6809 + $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
6810 + $checked = ($show_pdf_button === 'on') ? 'checked' : '';
6811 + // Check if the plugin is activated (paid feature)
6812 + $disabled = $this->is_activated ? '' : 'disabled';
6813 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6814 +
6815 + echo '<div class="' . esc_attr($class) . '">';
6816 + // Output the toggle switch
6817 + echo '<label class="toggle-switch">';
6818 + echo sprintf(
6819 + '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s %s />',
6820 + esc_attr($checked),
6821 + esc_attr($disabled)
6822 + );
6823 + echo '<span class="slider"></span>';
6824 + echo '</label>';
6825 +
6826 + // Pro feature overlay if needed
6827 + if (!$this->is_activated) {
6828 + echo '<div class="pro-feature-overlay">';
6829 + echo '<a href="https://mxchat.ai/" target="_blank">';
6830 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6831 + echo '</a>';
6832 + echo '</div>';
6833 + }
6834 +
6835 + echo '</div>';
6836 + echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
6837 +}
5140 6838
6839 +/**
6840 + * Callback for Word upload button toggle setting
6841 + */
6842 +public function mxchat_show_word_upload_button_callback() {
6843 + // Get toggle value with fallback
6844 + $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
6845 + $checked = ($show_word_button === 'on') ? 'checked' : '';
6846 + // Check if the plugin is activated (paid feature)
6847 + $disabled = $this->is_activated ? '' : 'disabled';
6848 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6849 +
6850 + echo '<div class="' . esc_attr($class) . '">';
6851 + // Output the toggle switch
6852 + echo '<label class="toggle-switch">';
6853 + echo sprintf(
6854 + '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s %s />',
6855 + esc_attr($checked),
6856 + esc_attr($disabled)
6857 + );
6858 + echo '<span class="slider"></span>';
6859 + echo '</label>';
6860 +
6861 + // Pro feature overlay if needed
6862 + if (!$this->is_activated) {
6863 + echo '<div class="pro-feature-overlay">';
6864 + echo '<a href="https://mxchat.ai/" target="_blank">';
6865 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6866 + echo '</a>';
6867 + echo '</div>';
6868 + }
6869 +
6870 + echo '</div>';
6871 + echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
6872 +}
6873 +
5141 6874 public function mxchat_pdf_intent_trigger_text_callback() {
5142 6875 $disabled = $this->is_activated ? '' : 'disabled';
5143 6876 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5144 6877 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
@@ -5359,24 +7092,21 @@
5359 7092 }
5360 7093 echo '</div>';
5361 7094 }
5362 7095
5363 -
5364 7096 public function mxchat_similarity_threshold_callback() {
5365 7097 // Load from mxchat_options array
5366 7098 $options = get_option('mxchat_options', []);
5367 -
5368 - // Get value with backwards compatibility
5369 - $threshold = isset($options['similarity_threshold'])
5370 - ? $options['similarity_threshold']
5371 - : get_option('mxchat_similarity_threshold', 80);
5372 -
7099 +
7100 + // Get value from options array with default of 80
7101 + $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
7102 +
5373 7103 echo '<div class="slider-container">';
5374 7104 echo sprintf(
5375 7105 '<input type="range"
5376 7106 id="similarity_threshold"
5377 7107 name="similarity_threshold"
5378 - min="70"
7108 + min="20"
5379 7109 max="85"
5380 7110 step="1"
5381 7111 value="%s"
5382 7112 class="range-slider" />',
@@ -5386,16 +7116,14 @@
5386 7116 '<span id="threshold_value" class="range-value">%s</span>',
5387 7117 esc_html($threshold)
5388 7118 );
5389 7119 echo '</div>';
5390 -
5391 7120 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');
7121 + echo esc_html__('Set the similarity threshold to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge. If your bot is not matching content, move to lowest setting to test.', 'mxchat');
5393 7122 echo '</p>';
5394 7123 }
5395 7124
5396 7125
5397 -
5398 7126 public function mxchat_live_agent_secret_key_callback() {
5399 7127 $disabled = $this->is_activated ? '' : 'disabled';
5400 7128 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5401 7129
@@ -5440,9 +7168,9 @@
5440 7168 public function mxchat_enqueue_admin_assets() {
5441 7169 wp_enqueue_style('wp-color-picker');
5442 7170
5443 7171 // Get the plugin version or file modification time for cache busting
5444 - $plugin_version = '2.0.3'; // Replace this with your plugin's version
7172 + $plugin_version = '2.1.6'; // Replace this with your plugin's version
5445 7173
5446 7174 // File paths
5447 7175 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5448 7176 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
@@ -5448,8 +7176,9 @@
5448 7176 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5449 7177 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5450 7178 $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5451 7179 $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
7180 + $transcripts_css_path = plugin_dir_path(__FILE__) . '../css/chat-transcripts.css';
5452 7181 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
5453 7182
5454 7183 // Check if files exist and get modification times
5455 7184 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
@@ -5456,10 +7185,38 @@
5456 7185 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5457 7186 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5458 7187 $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5459 7188 $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
7189 + $transcripts_css_version = file_exists($transcripts_css_path) ? filemtime($transcripts_css_path) : $plugin_version;
5460 7190 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5461 -
7191 +
7192 + $admin_status_js_path = plugin_dir_path(__FILE__) . '../js/admin-status.js';
7193 + $admin_status_js_version = file_exists($admin_status_js_path) ? filemtime($admin_status_js_path) : $plugin_version;
7194 +
7195 + // Check current admin page
7196 + $current_page = isset($_GET['page']) ? $_GET['page'] : '';
7197 +
7198 + // Only enqueue on the prompts page
7199 + if ($current_page === 'mxchat-prompts') {
7200 + wp_enqueue_script(
7201 + 'mxchat-status-updater',
7202 + plugin_dir_url(__FILE__) . '../js/admin-status.js',
7203 + array('jquery'),
7204 + $admin_status_js_version,
7205 + true
7206 + );
7207 +
7208 + // Add the nonce for the status updater
7209 + wp_localize_script(
7210 + 'mxchat-status-updater',
7211 + 'mxchat_status_data',
7212 + array(
7213 + 'ajax_url' => admin_url('admin-ajax.php'),
7214 + 'nonce' => wp_create_nonce('mxchat_status_nonce')
7215 + )
7216 + );
7217 + }
7218 +
5462 7219 // Enqueue scripts and styles with corrected paths
5463 7220 wp_enqueue_script(
5464 7221 'mxchat-color-picker',
5465 7222 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
@@ -5476,16 +7233,8 @@
5476 7233 true
5477 7234 );
5478 7235
5479 7236 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 7237 'mxchat-admin-js',
5489 7238 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5490 7239 array('jquery'),
5491 7240 $plugin_version,
@@ -5491,14 +7240,23 @@
5491 7240 $plugin_version,
5492 7241 true
5493 7242 );
5494 7243
5495 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
5496 - 'ajax_url' => admin_url('admin-ajax.php'),
5497 - 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5498 - 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5499 - 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5500 - ));
7244 +wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
7245 + 'ajax_url' => admin_url('admin-ajax.php'),
7246 + 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
7247 + 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
7248 + 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7249 + 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
7250 + 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7251 + 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
7252 + 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
7253 + 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7254 + 'is_activated' => $this->is_activated ? '1' : '0',
7255 + // Add these two new lines
7256 + 'status_nonce' => wp_create_nonce('mxchat_status_nonce'),
7257 + 'status_refresh_interval' => 5000, // Update every 5 seconds
7258 +));
5501 7259
5502 7260 // Enqueue the admin CSS
5503 7261 wp_enqueue_style(
5504 7262 'mxchat-admin-css',
@@ -5505,29 +7263,52 @@
5505 7263 plugin_dir_url(__FILE__) . '../css/admin-style.css',
5506 7264 array(),
5507 7265 $admin_css_version
5508 7266 );
7267 +
7268 + // Conditional enqueue for transcripts page
7269 + if ($current_page === 'mxchat-transcripts') {
7270 + wp_enqueue_style(
7271 + 'mxchat-chat-transcripts-css',
7272 + plugin_dir_url(__FILE__) . '../css/chat-transcripts.css',
7273 + array(),
7274 + $transcripts_css_version
7275 + );
7276 +
7277 + wp_enqueue_script(
7278 + 'mxchat-transcripts-js',
7279 + plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
7280 + array('jquery'),
7281 + $transcripts_js_version,
7282 + true
7283 + );
7284 + }
7285 +
7286 + // Only enqueue knowledge CSS on knowledge-related pages or all plugin pages
7287 + if (strpos($current_page, 'mxchat') !== false) {
7288 + wp_enqueue_style(
7289 + 'mxchat-knowledge-css',
7290 + plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
7291 + array(),
7292 + $knowledge_css_version
7293 + );
7294 + }
5509 7295
5510 - wp_enqueue_style(
5511 - 'mxchat-knowledge-css',
5512 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5513 - array(),
5514 - $knowledge_css_version
5515 - );
7296 + // Only enqueue intent-style.css on the mxchat-actions page
7297 + if ($current_page === 'mxchat-actions') {
7298 + wp_enqueue_style(
7299 + 'mxchat-intent-css',
7300 + plugin_dir_url(__FILE__) . '../css/intent-style.css',
7301 + array(),
7302 + $intent_css_version
7303 + );
7304 + }
5516 7305
5517 -
5518 - wp_enqueue_style(
5519 - 'mxchat-intent-css',
5520 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5521 - array(),
5522 - $intent_css_version
5523 - );
5524 -
5525 7306 // 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 - ) );
7307 + wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
7308 + 'ajax_url' => admin_url('admin-ajax.php'),
7309 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7310 + ));
5530 7311
5531 7312 // Localize the script for color picker and settings
5532 7313 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5533 7314 'ajax_url' => admin_url('admin-ajax.php'),
@@ -5557,8 +7338,10 @@
5557 7338 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5558 7339 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5559 7340 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5560 7341 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
7342 + 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
7343 + 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
5561 7344 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5562 7345 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5563 7346 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5564 7347 ));
@@ -5573,9 +7356,9 @@
5573 7356 }
5574 7357
5575 7358 if (isset($input['similarity_threshold'])) {
5576 7359 $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
7360 + $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5578 7361 }
5579 7362
5580 7363 if (isset($input['xai_api_key'])) {
5581 7364 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
@@ -5587,8 +7370,12 @@
5587 7370
5588 7371 if (isset($input['deepseek_api_key'])) {
5589 7372 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5590 7373 }
7374 +
7375 + if (isset($input['gemini_api_key'])) {
7376 + $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
7377 + }
5591 7378
5592 7379 if (isset($input['enable_woocommerce_integration'])) {
5593 7380 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5594 7381 }
@@ -5625,8 +7412,12 @@
5625 7412
5626 7413 if (isset($input['top_bar_title'])) {
5627 7414 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5628 7415 }
7416 +
7417 + if (isset($input['ai_agent_text'])) {
7418 + $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
7419 + }
5629 7420
5630 7421 if (isset($input['enable_email_block'])) {
5631 7422 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5632 7423 }
@@ -5639,9 +7430,9 @@
5639 7430 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5640 7431 }
5641 7432
5642 7433 if (isset($input['intro_message'])) {
5643 - $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
7434 + $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5644 7435 }
5645 7436
5646 7437 if (isset($input['input_copy'])) {
5647 7438 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
@@ -5679,24 +7470,50 @@
5679 7470
5680 7471 if (isset($input['pre_chat_message'])) {
5681 7472 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5682 7473 }
7474 +
7475 + if (isset($input['voyage_api_key'])) {
7476 + $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
7477 + }
7478 +
7479 + // Add to your sanitize function
7480 + if (isset($input['embedding_model'])) {
7481 + $allowed_models = array(
7482 + 'text-embedding-ada-002',
7483 + 'text-embedding-3-small',
7484 + 'text-embedding-3-large',
7485 + 'voyage-3-large'
7486 + );
7487 + if (in_array($input['embedding_model'], $allowed_models)) {
7488 + $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
7489 + }
7490 + }
5683 7491
5684 - if (isset($input['model'])) {
7492 + if (isset($input['model'])) {
5685 7493 $allowed_models = array(
5686 - 'grok-beta',
5687 - 'grok-2',
5688 - 'deepseek-chat',
5689 - 'claude-3-5-sonnet-20241022',
5690 - 'claude-3-opus-20240229',
5691 - 'claude-3-sonnet-20240229',
5692 - 'claude-3-haiku-20240307',
5693 - 'gpt-4o',
5694 - 'gpt-4o-mini',
5695 - 'gpt-4-turbo',
5696 - 'gpt-4',
5697 - 'gpt-3.5-turbo',
5698 - );
7494 + 'gemini-2.0-flash',
7495 + 'gemini-2.0-flash-lite',
7496 + 'gemini-1.5-pro',
7497 + 'gemini-1.5-flash',
7498 + 'grok-3-beta',
7499 + 'grok-3-fast-beta',
7500 + 'grok-3-mini-beta',
7501 + 'grok-3-mini-fast-beta',
7502 + 'grok-2',
7503 + 'deepseek-chat',
7504 + 'claude-3-7-sonnet-20250219',
7505 + 'claude-3-5-sonnet-20241022',
7506 + 'claude-3-opus-20240229',
7507 + 'claude-3-sonnet-20240229',
7508 + 'claude-3-haiku-20240307',
7509 + 'gpt-4o',
7510 + 'gpt-4.1-2025-04-14',
7511 + 'gpt-4o-mini',
7512 + 'gpt-4-turbo',
7513 + 'gpt-4',
7514 + 'gpt-3.5-turbo',
7515 + );
5699 7516 if (in_array($input['model'], $allowed_models)) {
5700 7517 $new_input['model'] = sanitize_text_field($input['model']);
5701 7518 }
5702 7519 }
@@ -5855,8 +7672,22 @@
5855 7672
5856 7673 if (isset($input['chat_toolbar_toggle'])) {
5857 7674 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5858 7675 }
7676 +
7677 + // Sanitize PDF upload button toggle
7678 + if (isset($input['show_pdf_upload_button'])) {
7679 + $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
7680 + } else {
7681 + $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
7682 + }
7683 +
7684 + // Sanitize Word upload button toggle
7685 + if (isset($input['show_word_upload_button'])) {
7686 + $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
7687 + } else {
7688 + $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
7689 + }
5859 7690
5860 7691 if (isset($input['pdf_intent_trigger_text'])) {
5861 7692 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5862 7693 }
@@ -5911,62 +7742,10 @@
5911 7742 }
5912 7743
5913 7744
5914 7745
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 7746
5921 - try {
5922 - $dom = new DOMDocument;
5923 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
5924 7747
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 7748 private function mxchat_fetch_loops_mailing_lists($api_key) {
5970 7749 $url = 'https://app.loops.so/api/v1/lists';
5971 7750 $response = wp_remote_get($url, array(
5972 7751 'headers' => array(
@@ -6011,8 +7790,81 @@
6011 7790 }
6012 7791 }
6013 7792
6014 7793
7794 +/**
7795 + * Update your existing display_admin_notices function to show notices on all MXChat pages
7796 + */
7797 +public function display_admin_notices() {
7798 + // Check if we're on a MXChat admin page
7799 + $screen = get_current_screen();
7800 + if (!$screen || strpos($screen->base, 'mxchat') === false) {
7801 + return;
7802 + }
7803 +
7804 + //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
7805 +
7806 + // Check for error notices
7807 + $error_notice = get_transient('mxchat_admin_notice_error');
7808 + if ($error_notice) {
7809 + //error_log('Found error transient: ' . $error_notice);
7810 + echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
7811 + delete_transient('mxchat_admin_notice_error');
7812 + //error_log('Displayed and deleted error transient');
7813 + } else {
7814 + //error_log('No error transient found');
7815 + }
7816 +
7817 + // Check for success notices
7818 + $success_notice = get_transient('mxchat_admin_notice_success');
7819 + if ($success_notice) {
7820 + //error_log('Found success transient: ' . $success_notice);
7821 + echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
7822 + delete_transient('mxchat_admin_notice_success');
7823 + //error_log('Displayed and deleted success transient');
7824 + }
7825 +
7826 + // Check for info notices
7827 + $info_notice = get_transient('mxchat_admin_notice_info');
7828 + if ($info_notice) {
7829 + //error_log('Found info transient: ' . $info_notice);
7830 + echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
7831 + delete_transient('mxchat_admin_notice_info');
7832 + //error_log('Displayed and deleted info transient');
7833 + }
7834 +
7835 + // Display active processing status
7836 + $this->display_processing_status();
7837 +}
7838 +
7839 +
7840 +
7841 +/**
7842 + * Display current processing status
7843 + */
7844 +private function display_processing_status() {
7845 + $pdf_url = get_transient('mxchat_last_pdf_url');
7846 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
7847 +
7848 + if (!$pdf_url && !$sitemap_url) {
7849 + return;
7850 + }
7851 +
7852 + $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
7853 + $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
7854 +
7855 + if ($sitemap_status && isset($sitemap_status['error']) && !empty($sitemap_status['error'])) {
7856 + echo '<div class="notice notice-error is-dismissible">';
7857 + echo '<p><strong>' . esc_html__('Sitemap Processing Error:', 'mxchat') . '</strong> ' . esc_html($sitemap_status['error']) . '</p>';
7858 + echo '</div>';
7859 + }
7860 +
7861 + if ($pdf_status && isset($pdf_status['error']) && !empty($pdf_status['error'])) {
7862 + echo '<div class="notice notice-error is-dismissible">';
7863 + echo '<p><strong>' . esc_html__('PDF Processing Error:', 'mxchat') . '</strong> ' . esc_html($pdf_status['error']) . '</p>';
7864 + echo '</div>';
7865 + }
7866 +}
6015 7867
6016 7868
6017 7869 }
6018 7870 ?>