PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.0
MxChat – AI Chatbot & Content Generation for WordPress v2.0.0
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 +911 -931 2.0.62.0.0 View file →
@@ -43,16 +43,14 @@
43 43 add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
44 44 add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 45 add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 46 add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 - add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 - add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
49 - add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
47 +
50 48 add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
51 49 if (isset($this->options['enable_woocommerce_integration']) &&
52 50 ($this->options['enable_woocommerce_integration'] === '1' ||
53 51 $this->options['enable_woocommerce_integration'] === 'on')) {
54 -
52 +
55 53 //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
56 54 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
57 55 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
58 56 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
@@ -71,14 +69,12 @@
71 69 'api_key' => '',
72 70 'xai_api_key' => '',
73 71 'claude_api_key' => '',
74 72 'deepseek_api_key' => '',
75 - 'voyage_api_key' => '',
76 - 'embedding_model' => 'text-embedding-ada-002',
77 73 '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:
78 74 - Your name is [Chatbot Name].
79 75 - 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.
80 - - 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.
76 + - Do not make up links to pages. Only use specific links you see in your knowledgebase.
81 77 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
82 78 - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'),
83 79 'model' => esc_html__('gpt-4o', 'mxchat'),
84 80 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
@@ -176,26 +172,28 @@
176 172 'mxchat-prompts',
177 173 array($this, 'mxchat_create_prompts_page')
178 174 );
179 175
176 + // Submenu page for Chat Transcripts
180 177 add_submenu_page(
181 - 'mxchat-max',
178 + 'mxchat-max', // Corrected parent slug to match the main menu
182 179 esc_html__('Chat Transcripts', 'mxchat'),
183 180 esc_html__('Transcripts', 'mxchat'),
184 181 'manage_options',
185 182 'mxchat-transcripts',
186 - array($this, 'mxchat_create_transcripts_page')
183 + array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
187 184 );
188 185
186 + // Submenu page for Intents
189 187 add_submenu_page(
190 - 'mxchat-max',
191 - esc_html__('MxChat Intents', 'mxchat'),
192 - esc_html__('Intents', 'mxchat'),
193 - 'manage_options',
194 - 'mxchat-intents',
195 - array($this, 'mxchat_intents_page_html')
188 + 'mxchat-max', // Parent slug
189 + esc_html__('MxChat Intents', 'mxchat'), // Page title
190 + esc_html__('Intents', 'mxchat'), // Menu title
191 + 'manage_options', // Capability
192 + 'mxchat-intents', // Menu slug
193 + array($this, 'mxchat_intents_page_html') // Callback function
196 194 );
197 -
195 +
198 196 add_submenu_page(
199 197 'mxchat-max',
200 198 esc_html__('Add Ons', 'mxchat'),
201 199 esc_html__('Add Ons', 'mxchat'),
@@ -243,8 +241,9 @@
243 241 // Also update old option for backwards compatibility
244 242 update_option('additional_popular_questions', $questions);
245 243 }
246 244 break;
245 + case 'close_button_color':
247 246 case 'user_message_bg_color':
248 247 case 'user_message_font_color':
249 248 case 'bot_message_bg_color':
250 249 case 'bot_message_font_color':
@@ -411,15 +410,16 @@
411 410 <?php endif; ?>
412 411
413 412 <div class="mxchat-agents-banner">
414 413 <p>
415 - <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'); ?>
416 - </p>
414 + <?php echo esc_html__('Pro users get exclusive access to', 'mxchat'); ?> <a href="https://mxchat.ai/agents/" target="_blank"><?php echo esc_html__('MxChat AI Agents', 'mxchat'); ?></a> <?php echo esc_html__('– our groundbreaking platform to test, refine, and deploy your chatbot with confidence. Take your AI assistant to the next level!', 'mxchat'); ?>
415 + </p>
417 416 </div>
418 417
419 418 <h2 class="mxchat-nav-tab-wrapper">
420 419 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
421 420 <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
421 + <a href="#theme" class="mxchat-nav-tab" data-tab="theme"><?php echo esc_html__('Theme', 'mxchat'); ?></a>
422 422 <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
423 423 </h2>
424 424
425 425 <div id="chatbot" class="mxchat-tab-content active">
@@ -429,8 +429,15 @@
429 429 </div>
430 430
431 431 <div id="embed" class="mxchat-tab-content">
432 432 <div class="mxchat-autosave-section">
433 + <div class="mxchat-settings-section">
434 + <h2><?php echo esc_html__('WooCommerce Settings', 'mxchat'); ?></h2>
435 + <table class="form-table">
436 + <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
437 + </table>
438 + </div>
439 + <div class="section-divider"></div>
433 440
434 441 <div class="mxchat-settings-section">
435 442 <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
436 443 <table class="form-table">
@@ -468,8 +475,13 @@
468 475 </div>
469 476 </div>
470 477 </div>
471 478
479 + <div id="theme" class="mxchat-tab-content">
480 + <div class="mxchat-autosave-section">
481 + <?php do_settings_sections('mxchat-theme'); ?>
482 + </div>
483 + </div>
472 484
473 485 <div id="general" class="mxchat-tab-content">
474 486 <?php do_settings_sections('mxchat-general'); ?>
475 487 <p>
@@ -578,8 +590,9 @@
578 590 <?php
579 591 }
580 592
581 593
594 +
582 595 public function mxchat_create_transcripts_page() {
583 596 global $wpdb;
584 597 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
585 598
@@ -585,341 +598,65 @@
585 598
586 599 // Get basic stats
587 600 $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
588 601 $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
589 -
590 - // Count unique users with detailed breakdown
591 - $total_users = $wpdb->get_var("
592 - SELECT COUNT(DISTINCT
593 - CASE
594 - WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
595 - WHEN user_id != 0 THEN CONCAT('user_', user_id)
596 - WHEN user_identifier NOT LIKE 'Tech-Savvy User'
597 - AND user_identifier NOT LIKE 'Detail-Oriented User'
598 - AND user_identifier NOT LIKE 'Language Learner'
599 - AND user_identifier NOT LIKE 'Casual Browser'
600 - AND user_identifier NOT LIKE 'Policy Enforcer'
601 - AND user_identifier NOT LIKE 'Researcher'
602 - AND user_identifier NOT LIKE 'Loyalty Member'
603 - AND user_identifier NOT LIKE 'Gift Buyer'
604 - AND user_identifier NOT LIKE 'Parent or Caregiver'
605 - THEN user_identifier
606 - ELSE session_id
607 - END
608 - )
609 - FROM $table_name
610 - WHERE role != 'assistant'
611 - ");
612 -
613 - // Get user type breakdown
614 - $registered_users = $wpdb->get_var("
615 - SELECT COUNT(DISTINCT user_email)
616 - FROM $table_name
617 - WHERE user_email != '' AND user_email IS NOT NULL
618 - ");
619 -
620 - $guest_users = $wpdb->get_var("
621 - SELECT COUNT(DISTINCT user_identifier)
622 - FROM $table_name
623 - WHERE (user_email = '' OR user_email IS NULL)
624 - AND role != 'assistant'
625 - AND user_identifier NOT LIKE 'Tech-Savvy User'
626 - AND user_identifier NOT LIKE 'Detail-Oriented User'
627 - AND user_identifier NOT LIKE 'Language Learner'
628 - AND user_identifier NOT LIKE 'Casual Browser'
629 - AND user_identifier NOT LIKE 'Policy Enforcer'
630 - AND user_identifier NOT LIKE 'Researcher'
631 - AND user_identifier NOT LIKE 'Loyalty Member'
632 - AND user_identifier NOT LIKE 'Gift Buyer'
633 - AND user_identifier NOT LIKE 'Parent or Caregiver'
634 - ");
635 -
636 - // Get agent test messages count
637 - $agent_tests = $wpdb->get_var("
638 - SELECT COUNT(DISTINCT session_id)
639 - FROM $table_name
640 - WHERE user_identifier IN (
641 - 'Tech-Savvy User',
642 - 'Detail-Oriented User',
643 - 'Language Learner',
644 - 'Casual Browser',
645 - 'Policy Enforcer',
646 - 'Researcher',
647 - 'Loyalty Member',
648 - 'Gift Buyer',
649 - 'Parent or Caregiver'
650 - )
651 - ");
602 + $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
652 603 ?>
653 - <div class="wrap mxchat-transcripts-wrapper">
654 - <!-- Hero Section -->
655 - <div class="mxchat-transcripts-hero">
656 - <h1 class="mxchat-main-title">
657 - Chat <span class="mxchat-gradient-text">Transcripts</span>
658 - </h1>
659 - <p class="mxchat-hero-subtitle">
660 - <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
661 - </p>
662 - </div>
663 - <div class="mxchat-content">
664 - <!-- Stats Cards -->
665 - <div class="mxchat-stats-grid">
666 - <div class="mxchat-stat-card">
667 - <div class="stat-icon">💬</div>
668 - <div class="stat-content">
669 - <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
670 - <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
671 - </div>
604 + <div class="wrap mxchat-admin">
605 + <!-- Stats Cards -->
606 + <div class="mxchat-stats-grid">
607 + <div class="mxchat-stat-card">
608 + <div class="stat-icon">💬</div>
609 + <div class="stat-content">
610 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
611 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
672 612 </div>
673 - <div class="mxchat-stat-card">
674 - <div class="stat-icon">📝</div>
675 - <div class="stat-content">
676 - <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
677 - <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
678 - </div>
613 + </div>
614 + <div class="mxchat-stat-card">
615 + <div class="stat-icon">📝</div>
616 + <div class="stat-content">
617 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
618 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
679 619 </div>
680 - <div class="mxchat-stat-card">
681 - <div class="stat-icon">👥</div>
682 - <div class="stat-content">
683 - <span class="stat-value"><?php echo esc_html($total_users); ?></span>
684 - <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
685 - <span class="stat-sublabel">
686 - <?php
687 - echo sprintf(
688 - esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
689 - $registered_users,
690 - $guest_users,
691 - $agent_tests
692 - );
693 - ?>
694 - </span>
695 - </div>
696 - </div>
697 620 </div>
698 - <!-- Search and Filter Controls -->
699 - <div class="mxchat-controls-wrapper">
700 - <div class="mxchat-search-box">
701 - <input type="text" id="mxchat-search-transcripts"
702 - placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
703 - class="regular-text">
621 + <div class="mxchat-stat-card">
622 + <div class="stat-icon">👥</div>
623 + <div class="stat-content">
624 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
625 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
704 626 </div>
705 - <form id="mxchat-delete-form" method="post">
706 - <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
707 - <div class="mxchat-controls">
708 - <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
709 - <span class="dashicons dashicons-download"></span>
710 - <?php esc_html_e('Export All Chats', 'mxchat'); ?>
711 - </button>
712 - <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
713 - <span class="dashicons dashicons-yes-alt"></span>
714 - <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
715 - </button>
716 - <button type="submit" class="button delete-chats-button">
717 - <span class="dashicons dashicons-trash"></span>
718 - <?php esc_html_e('Delete Selected', 'mxchat'); ?>
719 - </button>
720 - </div>
721 - </form>
722 627 </div>
723 - <!-- Transcripts Container -->
724 - <div id="mxchat-transcripts"></div>
725 628 </div>
726 - </div>
727 - <?php
728 -}
729 629
730 -public function export_chat_transcripts() {
731 - if (!current_user_can('manage_options')) {
732 - wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
733 - }
630 + <!-- Search and Filter Controls -->
631 + <div class="mxchat-controls-wrapper">
632 + <div class="mxchat-search-box">
633 + <input type="text" id="mxchat-search-transcripts"
634 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
635 + class="regular-text">
636 + </div>
734 637
735 - check_ajax_referer('mxchat_export_transcripts', 'security');
638 + <form id="mxchat-delete-form" method="post">
639 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
640 + <div class="mxchat-controls">
641 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
642 + <span class="dashicons dashicons-yes-alt"></span>
643 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
644 + </button>
645 + <button type="submit" class="button delete-chats-button">
646 + <span class="dashicons dashicons-trash"></span>
647 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
648 + </button>
649 + </div>
650 + </form>
651 + </div>
736 652
737 - global $wpdb;
738 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
739 -
740 - // Get all transcripts ordered by session and timestamp
741 - $results = $wpdb->get_results(
742 - "SELECT session_id, user_email, user_identifier, role, message, timestamp
743 - FROM {$table_name}
744 - ORDER BY session_id, timestamp ASC"
745 - );
746 -
747 - if (empty($results)) {
748 - wp_send_json_error(array('message' => 'No transcripts found.'));
749 - wp_die();
750 - }
751 -
752 - // Set headers for CSV download
753 - header('Content-Type: text/csv');
754 - header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
755 - header('Pragma: no-cache');
756 - header('Expires: 0');
757 -
758 - // Create output stream
759 - $output = fopen('php://output', 'w');
760 -
761 - // Add UTF-8 BOM for proper Excel encoding
762 - fputs($output, "\xEF\xBB\xBF");
763 -
764 - // Add CSV headers
765 - fputcsv($output, array(
766 - 'Session ID',
767 - 'Email',
768 - 'User Identifier',
769 - 'Role',
770 - 'Message',
771 - 'Timestamp'
772 - ));
773 -
774 - // Add data rows
775 - foreach ($results as $row) {
776 - fputcsv($output, array(
777 - $row->session_id,
778 - $row->user_email,
779 - $row->user_identifier,
780 - $row->role,
781 - $row->message,
782 - $row->timestamp
783 - ));
784 - }
785 -
786 - fclose($output);
787 - wp_die();
653 + <!-- Transcripts Container -->
654 + <div id="mxchat-transcripts"></div>
655 + </div>
656 + <?php
788 657 }
789 658
790 -public function mxchat_fetch_chat_history() {
791 - global $wpdb;
792 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
793 -
794 - if (!current_user_can('manage_options')) {
795 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
796 - }
797 -
798 - // First get unique session IDs ordered by most recent message in each session
799 - $session_ids = $wpdb->get_col(
800 - $wpdb->prepare(
801 - "SELECT DISTINCT session_id
802 - FROM {$table_name}
803 - GROUP BY session_id
804 - ORDER BY MAX(timestamp) DESC"
805 - )
806 - );
807 -
808 - if (empty($session_ids)) {
809 - wp_die(esc_html__('No chat history available.', 'mxchat'));
810 - }
811 -
812 - ob_start();
813 - echo '<div class="mxchat-transcript">';
814 -
815 - // Iterate through sessions from newest to oldest
816 - foreach ($session_ids as $session_id) {
817 - // Get the email associated with this session (if available)
818 - $email = $wpdb->get_var(
819 - $wpdb->prepare(
820 - "SELECT user_email
821 - FROM {$table_name}
822 - WHERE session_id = %s AND user_email != ''
823 - ORDER BY timestamp ASC
824 - LIMIT 1",
825 - $session_id
826 - )
827 - );
828 -
829 - // Debug: Check email retrieval
830 - if (empty($email)) {
831 - //error_log("No email found for session {$session_id}");
832 - } else {
833 - //error_log("Email for session {$session_id}: " . $email);
834 - }
835 -
836 - // Get messages for this session ordered by timestamp
837 - $messages = $wpdb->get_results(
838 - $wpdb->prepare(
839 - "SELECT * FROM {$table_name}
840 - WHERE session_id = %s
841 - ORDER BY timestamp ASC",
842 - $session_id
843 - )
844 - );
845 -
846 - // Start session block
847 - echo '<div class="mxchat-session">';
848 - echo '<div class="mxchat-session-header">';
849 - // Wrap checkbox and session ID in one block
850 - echo '<div class="mxchat-session-id">';
851 - echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
852 - echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
853 - echo '</div>';
854 -
855 - // Place email directly below the session ID block
856 - if (!empty($email)) {
857 - echo '<div class="mxchat-session-email">';
858 - echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
859 - echo '</div>';
860 - }
861 - echo '</div>';
862 -
863 - echo '<div class="mxchat-messages">';
864 -
865 - // Display messages for this session
866 - foreach ($messages as $transcript) {
867 - $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
868 -
869 - // Determine message styling
870 - switch ($transcript->role) {
871 - case 'assistant':
872 - case 'bot':
873 - $message_class = 'bot-message';
874 - $display_role = esc_html__('Chatbot', 'mxchat');
875 - break;
876 - case 'user':
877 - $message_class = 'user-message';
878 - $display_role = !empty($transcript->user_identifier)
879 - ? sanitize_text_field($transcript->user_identifier)
880 - : esc_html__('User', 'mxchat');
881 - break;
882 - case 'agent':
883 - $message_class = 'agent-message';
884 - $display_role = esc_html__('Agent', 'mxchat');
885 - break;
886 - default:
887 - $message_class = 'unknown-message';
888 - $display_role = esc_html__('Unknown', 'mxchat');
889 - }
890 -
891 - // Process message content
892 - $message_content = wp_kses(
893 - stripslashes($transcript->message),
894 - [
895 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
896 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
897 - 'a' => ['href' => [], 'title' => []]
898 - ]
899 - );
900 - $message_content = nl2br($message_content);
901 -
902 - // Render message
903 - echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
904 - echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
905 - echo '<div class="mxchat-message-content">' . $message_content . '</div>';
906 - echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
907 - echo '</div>';
908 - }
909 -
910 - // Close session block
911 - echo '</div></div>';
912 - }
913 -
914 - echo '</div>';
915 - $output = ob_get_clean();
916 - echo $output;
917 - wp_die();
918 -}
919 -
920 -
921 -
922 659 public function mxchat_create_prompts_page() {
923 660 global $wpdb;
924 661 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
925 662
@@ -952,9 +689,9 @@
952 689 $per_page,
953 690 $offset
954 691 )
955 692 );
956 -
693 +
957 694 // Add the pagination links generation here
958 695 $page_links = paginate_links(array(
959 696 'base' => add_query_arg(array(
960 697 'paged' => '%#%',
@@ -1010,9 +747,9 @@
1010 747 <!-- Auto-Sync Settings -->
1011 748 <div class="mxchat-settings-section">
1012 749 <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
1013 750 <p class="mxchat-description">
1014 - <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below. Works with Pinecone if enabled', 'mxchat'); ?>
751 + <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below.', 'mxchat'); ?>
1015 752 </p>
1016 753 <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
1017 754 <div class="mxchat-toggle-group">
1018 755 <div class="mxchat-toggle-container">
@@ -1018,11 +755,11 @@
1018 755 <div class="mxchat-toggle-container">
1019 756 <label class="mxchat-toggle-switch">
1020 757 <input type="checkbox"
1021 758 name="mxchat_auto_sync_posts"
1022 - class="mxchat-autosave-field"
759 + class="mxchat-autosave-field"
1023 760 value="1"
1024 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
761 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1025 762 <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
1026 763 <span class="mxchat-toggle-slider"></span>
1027 764 </label>
1028 765 <span class="mxchat-toggle-label">
@@ -1034,9 +771,9 @@
1034 771 <input type="checkbox"
1035 772 name="mxchat_auto_sync_pages"
1036 773 class="mxchat-autosave-field"
1037 774 value="1"
1038 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
775 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1039 776 <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
1040 777 <span class="mxchat-toggle-slider"></span>
1041 778 </label>
1042 779 <span class="mxchat-toggle-label">
@@ -1050,9 +787,10 @@
1050 787 <!-- Import Methods -->
1051 788 <div class="mxchat-import-methods">
1052 789 <div class="mxchat-method-card">
1053 790 <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1054 - <p><span class="red-warning">IMPORTANT:</span> <?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p> </div>
791 + <p><?php esc_html_e('Import all content from your sitemap automatically.', 'mxchat'); ?></p>
792 + </div>
1055 793 <div class="mxchat-method-card">
1056 794 <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1057 795 <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
1058 796 </div>
@@ -1068,9 +806,9 @@
1068 806
1069 807 <!-- Import Form -->
1070 808 <?php if (!$is_processing) : ?>
1071 809 <div class="mxchat-import-form">
1072 - <form id="mxchat-sitemap-form" method="post"
810 + <form id="mxchat-sitemap-form" method="post"
1073 811 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
1074 812 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
1075 813 <div class="mxchat-url-input-group">
1076 814 <input type="url"
@@ -1127,9 +865,9 @@
1127 865
1128 866 <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
1129 867 <div class="mxchat-status-card">
1130 868 <div class="mxchat-status-header">
1131 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
869 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
1132 870 <?php if ($is_processing) : ?>
1133 871 <form method="post" class="mxchat-stop-form"
1134 872 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1135 873 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
@@ -1172,19 +910,19 @@
1172 910 <div class="mxchat-card">
1173 911 <div class="mxchat-card-header">
1174 912 <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
1175 913 </div>
1176 - <form id="mxchat-content-form" method="post"
914 + <form id="mxchat-content-form" method="post"
1177 915 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
1178 916 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
1179 917 <div class="mxchat-form-group">
1180 918 <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
1181 - <textarea name="article_content" id="article_content" required
919 + <textarea name="article_content" id="article_content" required
1182 920 placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
1183 921 </div>
1184 922 <div class="mxchat-form-group">
1185 923 <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
1186 - <input type="url" name="article_url" id="article_url"
924 + <input type="url" name="article_url" id="article_url"
1187 925 placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
1188 926 </div>
1189 927 <button type="submit" name="submit_content" class="mxchat-button-primary">
1190 928 <?php esc_html_e('Submit Content', 'mxchat'); ?>
@@ -1258,13 +996,13 @@
1258 996 <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
1259 997 <?php endif; ?>
1260 998 </td>
1261 999 <td class="mxchat-actions-cell">
1262 - <button class="mxchat-button-icon edit-button"
1000 + <button class="mxchat-button-icon edit-button"
1263 1001 data-id="<?php echo esc_attr($prompt->id); ?>">
1264 1002 <span class="dashicons dashicons-edit"></span>
1265 1003 </button>
1266 - <button class="mxchat-button-icon save-button"
1004 + <button class="mxchat-button-icon save-button"
1267 1005 data-id="<?php echo esc_attr($prompt->id); ?>"
1268 1006 style="display:none;"
1269 1007 data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
1270 1008 <span class="dashicons dashicons-saved"></span>
@@ -1271,9 +1009,9 @@
1271 1009 </button>
1272 1010 <a href="<?php echo esc_url(admin_url(
1273 1011 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1274 1012 . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1275 - )); ?>"
1013 + )); ?>"
1276 1014 class="mxchat-button-icon delete-button"
1277 1015 onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1278 1016 <span class="dashicons dashicons-trash"></span>
1279 1017 </a>
@@ -1359,90 +1097,30 @@
1359 1097
1360 1098 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1361 1099 exit;
1362 1100 }
1363 -
1364 -
1365 1101 public function mxchat_generate_embedding($text) {
1366 - //error_log('Starting embedding generation for text: ' . substr($text, 0, 100) . '...');
1102 + $options = get_option('mxchat_options');
1103 + $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1367 1104
1368 - $options = get_option('mxchat_options');
1369 - $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
1370 - //error_log('Selected embedding model: ' . $selected_model);
1105 + $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1106 + 'body' => wp_json_encode(array(
1107 + 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1108 + 'input' => $text
1109 + )),
1110 + 'headers' => array(
1111 + 'Authorization' => 'Bearer ' . $api_key,
1112 + 'Content-Type' => esc_html__('application/json', 'mxchat')
1113 + ),
1114 + ));
1371 1115
1372 - // Determine provider and endpoint
1373 - if (strpos($selected_model, 'voyage') === 0) {
1374 - $api_key = $options['voyage_api_key'] ?? '';
1375 - $endpoint = 'https://api.voyageai.com/v1/embeddings';
1376 - } else {
1377 - $api_key = $options['api_key'] ?? '';
1378 - $endpoint = 'https://api.openai.com/v1/embeddings';
1379 - }
1380 - //error_log('Using endpoint: ' . $endpoint);
1116 + if (is_wp_error($response)) {
1117 + return null;
1118 + }
1381 1119
1382 - if (empty($api_key)) {
1383 - //error_log('Error: API key not configured');
1384 - return null;
1120 + $response_data = json_decode(wp_remote_retrieve_body($response), true);
1121 + return $response_data['data'][0]['embedding'] ?? null;
1385 1122 }
1386 -
1387 - // Prepare request body
1388 - $request_body = array(
1389 - 'model' => $selected_model,
1390 - 'input' => $text
1391 - );
1392 -
1393 - // Add output_dimension for voyage-3-large model
1394 - if ($selected_model === 'voyage-3-large') {
1395 - $request_body['output_dimension'] = 2048;
1396 - }
1397 -
1398 - //error_log('Request body prepared: ' . wp_json_encode($request_body));
1399 -
1400 - // Make API request
1401 - $response = wp_remote_post($endpoint, array(
1402 - 'body' => wp_json_encode($request_body),
1403 - 'headers' => array(
1404 - 'Authorization' => 'Bearer ' . $api_key,
1405 - 'Content-Type' => 'application/json'
1406 - ),
1407 - 'timeout' => 30
1408 - ));
1409 -
1410 - // Handle wp_remote_post errors
1411 - if (is_wp_error($response)) {
1412 - //error_log('WP Remote Post Error: ' . $response->get_error_message());
1413 - return null;
1414 - }
1415 -
1416 - // Get and check HTTP response code
1417 - $http_code = wp_remote_retrieve_response_code($response);
1418 - //error_log('API Response Code: ' . $http_code);
1419 -
1420 - if ($http_code !== 200) {
1421 - //error_log('API Error Response Body: ' . wp_remote_retrieve_body($response));
1422 - return null;
1423 - }
1424 -
1425 - // Parse response body
1426 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1427 -
1428 - if (json_last_error() !== JSON_ERROR_NONE) {
1429 - //error_log('JSON Parse Error: ' . json_last_error_msg());
1430 - return null;
1431 - }
1432 -
1433 - // Both APIs use the same structure, so we can extract the embedding the same way
1434 - if (isset($response_data['data'][0]['embedding'])) {
1435 - //error_log('Successfully extracted embedding');
1436 - //error_log('Embedding dimension: ' . count($response_data['data'][0]['embedding']));
1437 - return $response_data['data'][0]['embedding'];
1438 - } else {
1439 - //error_log('Error: No embedding found in response. Response structure: ' . wp_json_encode($response_data));
1440 - return null;
1441 - }
1442 -}
1443 -
1444 -
1445 1123 public function mxchat_delete_chat_history() {
1446 1124 if (!current_user_can('manage_options')) {
1447 1125 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1448 1126 wp_die();
@@ -1570,9 +1248,9 @@
1570 1248
1571 1249 if ($pinecone_settings && is_array($pinecone_settings)) {
1572 1250 // Check if Pinecone is enabled and all required settings are present
1573 1251 $use_pinecone = (
1574 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1252 + isset($pinecone_settings['mxchat_use_pinecone']) &&
1575 1253 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1576 1254 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1577 1255 !empty($pinecone_settings['mxchat_pinecone_host']) &&
1578 1256 !empty($pinecone_settings['mxchat_pinecone_index']) &&
@@ -1591,9 +1269,9 @@
1591 1269 $pinecone_settings['mxchat_pinecone_index']
1592 1270 );
1593 1271
1594 1272 if (!$pinecone_result['success']) {
1595 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1273 + error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1596 1274 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1597 1275 }
1598 1276 } else {
1599 1277 // Fallback to WordPress DB storage
@@ -1599,25 +1277,28 @@
1599 1277 // Fallback to WordPress DB storage
1600 1278 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1601 1279 }
1602 1280 }
1603 -// Modified storage function to add type field
1281 +
1604 1282 private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1283 + // Generate vector ID based on URL for consistent updates/inserts
1605 1284 $vector_id = $vector_id ?: md5($url);
1285 +
1286 + // Get the Pinecone host from options
1606 1287 $options = get_option('mxchat_pinecone_addon_options');
1607 1288 $host = $options['mxchat_pinecone_host'] ?? '';
1608 -
1289 +
1609 1290 if (empty($host)) {
1291 + //error_log('Pinecone host is not configured');
1610 1292 return array(
1611 1293 'success' => false,
1612 1294 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1613 1295 );
1614 1296 }
1615 -
1616 - // Determine if this is a product URL
1617 - $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1618 -
1297 +
1298 + // Build API endpoint using the configured host
1619 1299 $api_endpoint = "https://{$host}/vectors/upsert";
1300 +
1620 1301 $request_body = array(
1621 1302 'vectors' => array(
1622 1303 array(
1623 1304 'id' => $vector_id,
@@ -1624,15 +1305,14 @@
1624 1305 'values' => $embedding_vector,
1625 1306 'metadata' => array(
1626 1307 'text' => $content,
1627 1308 'source_url' => $url,
1628 - 'type' => $is_product ? 'product' : 'content',
1629 1309 'last_updated' => time()
1630 1310 )
1631 1311 )
1632 1312 )
1633 1313 );
1634 -
1314 +
1635 1315 $response = wp_remote_post($api_endpoint, array(
1636 1316 'headers' => array(
1637 1317 'Api-Key' => $api_key,
1638 1318 'accept' => 'application/json',
@@ -1641,24 +1321,27 @@
1641 1321 'body' => wp_json_encode($request_body),
1642 1322 'timeout' => 30,
1643 1323 'data_format' => 'body'
1644 1324 ));
1645 -
1325 +
1646 1326 if (is_wp_error($response)) {
1327 + //error_log('Pinecone API WP Error: ' . $response->get_error_message());
1647 1328 return array(
1648 1329 'success' => false,
1649 1330 'message' => $response->get_error_message()
1650 1331 );
1651 1332 }
1652 -
1333 +
1653 1334 $response_code = wp_remote_retrieve_response_code($response);
1654 1335 if ($response_code !== 200) {
1655 1336 $body = wp_remote_retrieve_body($response);
1337 + //error_log('Pinecone API Error Body: ' . $body);
1338 + //error_log('Pinecone API Request: ' . wp_json_encode($request_body));
1656 1339 return array(
1657 1340 'success' => false,
1658 1341 'message' => sprintf(
1659 - 'Pinecone API error (HTTP %d): %s',
1660 - $response_code,
1342 + 'Pinecone API error (HTTP %d): %s',
1343 + $response_code,
1661 1344 $body
1662 1345 )
1663 1346 );
1664 1347 }
@@ -1667,8 +1350,10 @@
1667 1350 'success' => true,
1668 1351 'message' => 'Successfully stored in Pinecone'
1669 1352 );
1670 1353 }
1354 +
1355 +
1671 1356 // Helper method for WordPress DB storage
1672 1357 private function store_in_wordpress_db($content, $url, $embedding_vector) {
1673 1358 global $wpdb;
1674 1359 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
@@ -1700,109 +1385,16 @@
1700 1385 array('%s', '%s', '%s', '%s')
1701 1386 );
1702 1387 }
1703 1388 }
1704 -/**
1705 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1706 - *
1707 - * @param int $post_id The ID of the post being deleted
1708 - * @return void
1709 - */
1710 -public function mxchat_handle_post_delete($post_id) {
1711 - // Get post data before it's deleted
1712 - $post = get_post($post_id);
1713 1389
1714 - // Basic validation
1715 - if (!$post || wp_is_post_revision($post_id)) {
1716 - return;
1717 - }
1718 1390
1719 - // Check post type
1720 - $post_type = $post->post_type;
1721 - if (!in_array($post_type, array('post', 'page'))) {
1722 - return;
1723 - }
1724 -
1725 - // Check if sync is enabled for this post type
1726 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1727 - if (get_option($sync_option) != '1') {
1728 - return;
1729 - }
1730 -
1731 - // Get the URL before post is deleted
1732 - $source_url = get_permalink($post_id);
1733 - if (!$source_url) {
1734 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1735 - return;
1736 - }
1737 -
1738 - // Check for Pinecone addon and its settings
1739 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1740 - $use_pinecone = false;
1741 - if ($pinecone_settings && is_array($pinecone_settings)) {
1742 - $use_pinecone = (
1743 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1744 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1745 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1746 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1747 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1748 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1749 - );
1750 - }
1751 -
1752 - $deletion_successful = false;
1753 -
1754 - if ($use_pinecone) {
1755 - try {
1756 - $pinecone_result = $this->delete_from_pinecone(
1757 - array($source_url),
1758 - $pinecone_settings['mxchat_pinecone_api_key'],
1759 - $pinecone_settings['mxchat_pinecone_environment'],
1760 - $pinecone_settings['mxchat_pinecone_index']
1761 - );
1762 -
1763 - if (!$pinecone_result['success']) {
1764 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1765 - } else {
1766 - $deletion_successful = true;
1767 - }
1768 - } catch (Exception $e) {
1769 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1770 - }
1771 - }
1772 -
1773 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1774 - try {
1775 - global $wpdb;
1776 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1777 -
1778 - $result = $wpdb->delete(
1779 - $table_name,
1780 - array('source_url' => $source_url),
1781 - array('%s')
1782 - );
1783 -
1784 - if ($result === false) {
1785 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1786 - } else {
1787 - $deletion_successful = true;
1788 - }
1789 - } catch (Exception $e) {
1790 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1791 - }
1792 -
1793 - if (!$deletion_successful) {
1794 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1795 - }
1796 -}
1797 -
1798 -
1799 1391 public function mxchat_handle_content_submission() {
1800 1392 // Check if the form was submitted and the user has permission.
1801 1393 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1802 1394 return;
1803 1395 }
1804 -
1396 +
1805 1397 // Verify the nonce.
1806 1398 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1807 1399 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1808 1400 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
@@ -1814,10 +1406,10 @@
1814 1406
1815 1407 // Generate the embedding vector.
1816 1408 $embedding_vector = $this->mxchat_generate_embedding($article_content);
1817 1409 if (!is_array($embedding_vector)) {
1818 - set_transient('mxchat_admin_notice_error',
1819 - esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1410 + set_transient('mxchat_admin_notice_error',
1411 + esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1820 1412 30
1821 1413 );
1822 1414 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1823 1415 exit;
@@ -1844,15 +1436,15 @@
1844 1436 array('%s', '%s', '%s')
1845 1437 );
1846 1438
1847 1439 if ($inserted === false) {
1848 - set_transient('mxchat_admin_notice_error',
1849 - esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1440 + set_transient('mxchat_admin_notice_error',
1441 + esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1850 1442 30
1851 1443 );
1852 1444 } else {
1853 - set_transient('mxchat_admin_notice_success',
1854 - esc_html__('Content successfully submitted!', 'mxchat'),
1445 + set_transient('mxchat_admin_notice_success',
1446 + esc_html__('Content successfully submitted!', 'mxchat'),
1855 1447 30
1856 1448 );
1857 1449 }
1858 1450
@@ -2037,11 +1629,9 @@
2037 1629
2038 1630 return array(
2039 1631 'total_pages' => absint($status['total_pages']),
2040 1632 'processed_pages' => absint($status['processed_pages']),
2041 - 'percentage' => ($status['total_pages'] > 0)
2042 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
2043 - : 0,
1633 + 'percentage' => round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100),
2044 1634 'status' => sanitize_text_field($status['status']),
2045 1635 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
2046 1636 );
2047 1637 }
@@ -2250,25 +1840,25 @@
2250 1840 $sitemap_url = esc_url_raw($sitemap_url);
2251 1841 $total_urls = absint($total_urls);
2252 1842 $batch_size = absint($batch_size);
2253 1843 $batch_pause = absint($batch_pause);
2254 -
1844 +
2255 1845 if (!is_array($urls) || empty($urls)) {
2256 1846 return;
2257 1847 }
2258 -
1848 +
2259 1849 try {
2260 1850 $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
2261 1851 $status = get_transient($status_key);
2262 -
1852 +
2263 1853 if (!$status || !is_array($status)) {
2264 1854 throw new Exception('Invalid status data retrieved from transient');
2265 1855 }
2266 -
1856 +
2267 1857 $start_url = absint($status['processed_urls']);
2268 1858 $end_url = min($start_url + $batch_size, $total_urls);
2269 1859 $instance = new self();
2270 -
1860 +
2271 1861 // Track failures in batch
2272 1862 $batch_stats = [
2273 1863 'processed' => 0,
2274 1864 'failed' => 0,
@@ -2277,43 +1867,43 @@
2277 1867
2278 1868 for ($i = $start_url; $i < $end_url; $i++) {
2279 1869 $page_url = esc_url_raw($urls[$i]);
2280 1870 $page_response = wp_remote_get($page_url);
2281 -
1871 +
2282 1872 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
2283 1873 $batch_stats['failed']++;
2284 1874 continue;
2285 1875 }
2286 -
1876 +
2287 1877 $page_html = wp_remote_retrieve_body($page_response);
2288 1878 $page_content = $instance->mxchat_extract_main_content($page_html);
2289 1879 $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2290 -
1880 +
2291 1881 if (!empty($sanitized_content)) {
2292 1882 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2293 -
1883 +
2294 1884 if (is_array($embedding_vector)) {
2295 1885 $options = get_option('mxchat_options');
2296 1886 $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2297 -
1887 +
2298 1888 if (is_wp_error($submission_result)) {
2299 1889 $batch_stats['failed']++;
2300 1890 $batch_stats['last_error'] = $submission_result->get_error_message();
2301 1891 continue;
2302 1892 }
2303 -
1893 +
2304 1894 $batch_stats['processed']++;
2305 1895 } else {
2306 1896 $batch_stats['failed']++;
2307 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check embedding API key.';
1897 + $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
2308 1898 }
2309 1899 }
2310 -
1900 +
2311 1901 $status['processed_urls'] = absint($i + 1);
2312 1902 $status['last_update'] = time();
2313 1903 $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
2314 1904 $status['last_error'] = $batch_stats['last_error'];
2315 -
1905 +
2316 1906 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2317 1907 }
2318 1908
2319 1909 // If all URLs in this batch failed, stop processing
@@ -2349,20 +1939,18 @@
2349 1939
2350 1940 public function get_sitemap_processing_status($sitemap_url) {
2351 1941 $sitemap_url = esc_url_raw($sitemap_url);
2352 1942 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2353 -
1943 +
2354 1944 if (!$status || !is_array($status)) {
2355 1945 return false;
2356 1946 }
2357 -
1947 +
2358 1948 return array(
2359 1949 'total_urls' => absint($status['total_urls']),
2360 1950 'processed_urls' => absint($status['processed_urls']),
2361 1951 'failed_urls' => absint($status['failed_urls'] ?? 0),
2362 - 'percentage' => ($status['total_urls'] > 0)
2363 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2364 - : 0,
1952 + 'percentage' => round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100),
2365 1953 'status' => sanitize_text_field($status['status']),
2366 1954 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2367 1955 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2368 1956 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
@@ -2468,9 +2056,9 @@
2468 2056 $use_pinecone = false;
2469 2057
2470 2058 if ($pinecone_settings && is_array($pinecone_settings)) {
2471 2059 $use_pinecone = (
2472 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2060 + isset($pinecone_settings['mxchat_use_pinecone']) &&
2473 2061 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2474 2062 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2475 2063 !empty($pinecone_settings['mxchat_pinecone_host']) &&
2476 2064 !empty($pinecone_settings['mxchat_pinecone_index']) &&
@@ -2501,11 +2089,11 @@
2501 2089 public function mxchat_handle_product_delete($post_id) {
2502 2090 if (get_post_type($post_id) !== 'product') {
2503 2091 return;
2504 2092 }
2505 -
2093 +
2506 2094 $source_url = get_permalink($post_id);
2507 -
2095 +
2508 2096 // Check for Pinecone addon and its settings
2509 2097 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2510 2098 $use_pinecone = false;
2511 2099
@@ -2510,9 +2098,9 @@
2510 2098 $use_pinecone = false;
2511 2099
2512 2100 if ($pinecone_settings && is_array($pinecone_settings)) {
2513 2101 $use_pinecone = (
2514 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2102 + isset($pinecone_settings['mxchat_use_pinecone']) &&
2515 2103 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2516 2104 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2517 2105 !empty($pinecone_settings['mxchat_pinecone_host']) &&
2518 2106 !empty($pinecone_settings['mxchat_pinecone_index']) &&
@@ -2534,102 +2122,139 @@
2534 2122 $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2535 2123 }
2536 2124 }
2537 2125
2126 +public function mxchat_fetch_chat_history() {
2127 + global $wpdb;
2128 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2538 2129
2539 -/**
2540 - * Delete vectors from Pinecone by source URL
2541 - *
2542 - * @param array $urls Array of source URLs to delete
2543 - * @param string $api_key Pinecone API key
2544 - * @param string $environment Pinecone environment
2545 - * @param string $index_name Pinecone index name
2546 - * @return array Associative array with 'success' boolean and 'message' string
2547 - */
2548 -private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2549 - // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2550 - $options = get_option('mxchat_pinecone_addon_options');
2551 - $host = $options['mxchat_pinecone_host'] ?? '';
2552 -
2553 - if (empty($host)) {
2554 - return array(
2555 - 'success' => false,
2556 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2557 - );
2130 + if (!current_user_can('manage_options')) {
2131 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2558 2132 }
2559 2133
2560 - // Build API endpoint using the configured host
2561 - $api_endpoint = "https://{$host}/vectors/delete";
2562 -
2563 - // Create vector IDs from URLs (matching your store method's ID generation)
2564 - $vector_ids = array_map('md5', $urls);
2565 -
2566 - // Prepare the delete request body
2567 - $request_body = array(
2568 - 'ids' => $vector_ids,
2569 - 'filter' => array(
2570 - 'source_url' => array(
2571 - '$in' => $urls
2572 - )
2134 + // First get unique session IDs ordered by most recent message in each session
2135 + $session_ids = $wpdb->get_col(
2136 + $wpdb->prepare(
2137 + "SELECT DISTINCT session_id
2138 + FROM {$table_name}
2139 + GROUP BY session_id
2140 + ORDER BY MAX(timestamp) DESC"
2573 2141 )
2574 2142 );
2575 2143
2576 - // Make the deletion request
2577 - $response = wp_remote_post($api_endpoint, array(
2578 - 'headers' => array(
2579 - 'Api-Key' => $api_key,
2580 - 'accept' => 'application/json',
2581 - 'content-type' => 'application/json'
2582 - ),
2583 - 'body' => wp_json_encode($request_body),
2584 - 'timeout' => 30,
2585 - 'data_format' => 'body'
2586 - ));
2144 + if (empty($session_ids)) {
2145 + wp_die(esc_html__('No chat history available.', 'mxchat'));
2146 + }
2587 2147
2588 - // Handle WordPress HTTP API errors
2589 - if (is_wp_error($response)) {
2590 - return array(
2591 - 'success' => false,
2592 - 'message' => $response->get_error_message()
2148 + ob_start();
2149 + echo '<div class="mxchat-transcript">';
2150 +
2151 + // Iterate through sessions from newest to oldest
2152 + foreach ($session_ids as $session_id) {
2153 + // Get the email associated with this session (if available)
2154 + $email = $wpdb->get_var(
2155 + $wpdb->prepare(
2156 + "SELECT user_email
2157 + FROM {$table_name}
2158 + WHERE session_id = %s AND user_email != ''
2159 + ORDER BY timestamp ASC
2160 + LIMIT 1",
2161 + $session_id
2162 + )
2593 2163 );
2594 - }
2595 2164
2596 - // Check response status
2597 - $response_code = wp_remote_retrieve_response_code($response);
2598 - if ($response_code !== 200) {
2599 - $body = wp_remote_retrieve_body($response);
2600 - return array(
2601 - 'success' => false,
2602 - 'message' => sprintf(
2603 - 'Pinecone API error (HTTP %d): %s',
2604 - $response_code,
2605 - $body
2165 + // Debug: Check email retrieval
2166 + if (empty($email)) {
2167 + //error_log("No email found for session {$session_id}");
2168 + } else {
2169 + //error_log("Email for session {$session_id}: " . $email);
2170 + }
2171 +
2172 + // Get messages for this session ordered by timestamp
2173 + $messages = $wpdb->get_results(
2174 + $wpdb->prepare(
2175 + "SELECT * FROM {$table_name}
2176 + WHERE session_id = %s
2177 + ORDER BY timestamp ASC",
2178 + $session_id
2606 2179 )
2607 2180 );
2608 - }
2609 2181
2610 - // Parse response body
2611 - $body = wp_remote_retrieve_body($response);
2612 - $response_data = json_decode($body, true);
2182 + // Start session block
2183 + echo '<div class="mxchat-session">';
2184 + echo '<div class="mxchat-session-header">';
2185 + // Wrap checkbox and session ID in one block
2186 + echo '<div class="mxchat-session-id">';
2187 + echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
2188 + echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
2189 + echo '</div>';
2613 2190
2614 - // Final validation of the response
2615 - if (json_last_error() !== JSON_ERROR_NONE) {
2616 - return array(
2617 - 'success' => false,
2618 - 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2619 - );
2620 - }
2191 + // Place email directly below the session ID block
2192 + if (!empty($email)) {
2193 + echo '<div class="mxchat-session-email">';
2194 + echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
2195 + echo '</div>';
2196 + }
2197 + echo '</div>';
2621 2198
2622 - return array(
2623 - 'success' => true,
2624 - 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2625 - );
2626 -}
2199 + echo '<div class="mxchat-messages">';
2627 2200
2201 + // Display messages for this session
2202 + foreach ($messages as $transcript) {
2203 + $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
2628 2204
2205 + // Determine message styling
2206 + switch ($transcript->role) {
2207 + case 'assistant':
2208 + case 'bot':
2209 + $message_class = 'bot-message';
2210 + $display_role = esc_html__('Chatbot', 'mxchat');
2211 + break;
2212 + case 'user':
2213 + $message_class = 'user-message';
2214 + $display_role = !empty($transcript->user_identifier)
2215 + ? sanitize_text_field($transcript->user_identifier)
2216 + : esc_html__('User', 'mxchat');
2217 + break;
2218 + case 'agent':
2219 + $message_class = 'agent-message';
2220 + $display_role = esc_html__('Agent', 'mxchat');
2221 + break;
2222 + default:
2223 + $message_class = 'unknown-message';
2224 + $display_role = esc_html__('Unknown', 'mxchat');
2225 + }
2629 2226
2227 + // Process message content
2228 + $message_content = wp_kses(
2229 + stripslashes($transcript->message),
2230 + [
2231 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2232 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2233 + 'a' => ['href' => [], 'title' => []]
2234 + ]
2235 + );
2236 + $message_content = nl2br($message_content);
2630 2237
2238 + // Render message
2239 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
2240 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
2241 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
2242 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
2243 + echo '</div>';
2244 + }
2631 2245
2246 + // Close session block
2247 + echo '</div></div>';
2248 + }
2249 +
2250 + echo '</div>';
2251 + $output = ob_get_clean();
2252 + echo $output;
2253 + wp_die();
2254 +}
2255 +
2256 +
2632 2257 public function mxchat_create_activation_page() {
2633 2258 $license_status = get_option('mxchat_license_status', 'inactive');
2634 2259 $license_error = get_option('mxchat_license_error', '');
2635 2260 ?>
@@ -2674,15 +2299,15 @@
2674 2299 </div>
2675 2300 </form>
2676 2301 <!-- License Status Display -->
2677 2302 <div class="mxchat-pro-status">
2678 - <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2303 + <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2679 2304 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2680 2305 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2681 2306 </span>
2682 2307 </h3>
2683 2308 </div>
2684 -
2309 +
2685 2310 </div>
2686 2311 <?php
2687 2312 }
2688 2313
@@ -2711,9 +2336,9 @@
2711 2336 $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
2712 2337
2713 2338 if ($search_term) {
2714 2339 $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
2715 - $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
2340 + $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
2716 2341 $search_term_like, $search_term_like);
2717 2342 }
2718 2343
2719 2344 if ($callback_filter) {
@@ -2725,9 +2350,9 @@
2725 2350 $total_pages = ceil($total_intents / $per_page);
2726 2351
2727 2352 // Get intents
2728 2353 $intents = $wpdb->get_results($wpdb->prepare(
2729 - "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2354 + "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2730 2355 $per_page, $offset
2731 2356 ));
2732 2357
2733 2358 // Get callbacks
@@ -2750,9 +2375,9 @@
2750 2375 <div class="mxchat-card">
2751 2376 <div class="mxchat-card-header">
2752 2377 <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2753 2378 </div>
2754 -
2379 +
2755 2380 <div class="mxchat-intent-documentation">
2756 2381 <p>
2757 2382 <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2758 2383 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
@@ -2761,9 +2386,9 @@
2761 2386 <?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'); ?>
2762 2387 </p>
2763 2388 </div>
2764 2389
2765 - <form id="mxchat-add-intent-form" method="post"
2390 + <form id="mxchat-add-intent-form" method="post"
2766 2391 action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2767 2392 <input type="hidden" name="action" value="mxchat_add_intent">
2768 2393 <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2769 2394
@@ -2788,9 +2413,9 @@
2788 2413 <div class="mxchat-form-group">
2789 2414 <label for="callback_function">
2790 2415 <?php esc_html_e('Callback Function', 'mxchat'); ?>
2791 2416 </label>
2792 - <select name="callback_function" id="callback_function"
2417 + <select name="callback_function" id="callback_function"
2793 2418 class="mxchat-intent-select" required>
2794 2419 <option value="">
2795 2420 <?php esc_html_e('Select a Callback', 'mxchat'); ?>
2796 2421 </option>
@@ -2803,9 +2428,9 @@
2803 2428 $pro_only = $data['pro_only'];
2804 2429 $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2805 2430 $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2806 2431 ?>
2807 - <option value="<?php echo esc_attr($function); ?>"
2432 + <option value="<?php echo esc_attr($function); ?>"
2808 2433 <?php echo $disabled; ?>>
2809 2434 <?php echo esc_html($label . $label_suffix); ?>
2810 2435 </option>
2811 2436 <?php
@@ -2830,9 +2455,9 @@
2830 2455 <form method="get" class="mxchat-search-form">
2831 2456 <input type="hidden" name="page" value="mxchat-intents">
2832 2457 <div class="mxchat-search-group">
2833 2458 <span class="dashicons dashicons-search"></span>
2834 - <input type="text" name="s"
2459 + <input type="text" name="s"
2835 2460 placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2836 2461 value="<?php echo esc_attr($search_term); ?>">
2837 2462 <select name="callback_filter" class="mxchat-intent-filter">
2838 2463 <option value="">
@@ -2839,9 +2464,9 @@
2839 2464 <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2840 2465 </option>
2841 2466 <?php foreach ($available_callbacks as $function => $callback_data) :
2842 2467 $label = $callback_data['label']; ?>
2843 - <option value="<?php echo esc_attr($function); ?>"
2468 + <option value="<?php echo esc_attr($function); ?>"
2844 2469 <?php selected($callback_filter, $function); ?>>
2845 2470 <?php echo esc_html($label); ?>
2846 2471 </option>
2847 2472 <?php endforeach; ?>
@@ -2864,123 +2489,83 @@
2864 2489 <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2865 2490 <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2866 2491 </tr>
2867 2492 </thead>
2868 - <tbody>
2869 - <?php if ($intents) :
2870 - foreach ($intents as $intent) :
2871 - $callback_function = $intent->callback_function;
2872 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2873 - ? $available_callbacks[$callback_function]['label']
2874 - : $callback_function;
2875 - $threshold_value = isset($intent->similarity_threshold)
2876 - ? round($intent->similarity_threshold * 100)
2877 - : 85;
2878 -
2879 - // Check if this is a form intent (its intent_label starts with "Form ")
2880 - $is_form_intent = strpos($intent->intent_label, 'Form ') === 0;
2881 - ?>
2882 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2883 - <td>
2884 - <?php
2885 - if ($is_form_intent) {
2886 - // Attempt to extract the form ID from the intent_label.
2887 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2888 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2889 - if ($form_id) {
2890 - global $wpdb;
2891 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2892 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2893 - if ($form && !empty($form->title)) {
2894 - echo esc_html($form->title);
2895 - } else {
2896 - echo esc_html($intent->intent_label);
2897 - }
2898 - } else {
2899 - echo esc_html($intent->intent_label);
2900 - }
2901 - echo ' <span class="mxchat-badge">Form Intent</span>';
2902 - } else {
2903 - echo esc_html($intent->intent_label);
2904 - }
2905 - ?>
2906 - </td>
2907 - <td class="mxchat-content-cell">
2908 - <?php echo esc_html($intent->phrases); ?>
2909 - </td>
2910 - <td><?php echo esc_html($callback_label); ?></td>
2911 - <td>
2912 - <!-- Similarity threshold adjustment (shown for all intents) -->
2913 - <form method="post"
2914 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2915 - class="mxchat-threshold-form">
2916 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2917 - <input type="hidden" name="action"
2918 - value="mxchat_update_intent_threshold">
2919 - <input type="hidden" name="intent_id"
2920 - value="<?php echo esc_attr($intent->id); ?>">
2921 - <div class="mxchat-slider-group">
2922 - <input type="range"
2923 - name="intent_threshold"
2924 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2925 - min="70"
2926 - max="95"
2927 - value="<?php echo esc_attr($threshold_value); ?>"
2928 - class="mxchat-intent-slider"
2929 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2930 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2931 - class="mxchat-intent-output">
2932 - <?php echo esc_html($threshold_value); ?>%
2933 - </output>
2934 - </div>
2935 - <button type="submit" class="mxchat-button-icon">
2936 - <span class="dashicons dashicons-saved"></span>
2937 - </button>
2938 - </form>
2939 - </td>
2940 - <td class="mxchat-actions-cell">
2941 - <?php if (!$is_form_intent) : ?>
2942 - <!-- Standard intents: allow edit and delete -->
2943 - <button type="button"
2944 - class="mxchat-button-icon mxchat-edit-button"
2945 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2946 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2947 - <span class="dashicons dashicons-edit"></span>
2948 - </button>
2949 - <form method="post"
2950 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2951 - class="mxchat-delete-form"
2952 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2953 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2954 - <input type="hidden" name="action" value="mxchat_delete_intent">
2955 - <input type="hidden" name="intent_id"
2956 - value="<?php echo esc_attr($intent->id); ?>">
2957 - <button type="submit" class="mxchat-button-icon">
2958 - <span class="dashicons dashicons-trash"></span>
2959 - </button>
2960 - </form>
2961 - <?php else : ?>
2962 - <!-- Form intents: show manage in forms button -->
2963 - <?php
2964 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2965 - $form_id = isset($matches[1]) ? $matches[1] : '';
2966 - ?>
2967 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2968 - class="mxchat-button-secondary">
2969 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2970 - </a>
2971 - <?php endif; ?>
2972 - </td>
2973 - </tr>
2974 - <?php endforeach;
2975 - else : ?>
2493 + <tbody>
2494 + <?php if ($intents) :
2495 + foreach ($intents as $intent) :
2496 + $callback_function = $intent->callback_function;
2497 + $callback_label = isset($available_callbacks[$callback_function]['label'])
2498 + ? $available_callbacks[$callback_function]['label']
2499 + : $callback_function;
2500 + $threshold_value = isset($intent->similarity_threshold)
2501 + ? round($intent->similarity_threshold * 100)
2502 + : 85;
2503 + ?>
2976 2504 <tr>
2977 - <td colspan="5" class="mxchat-no-records">
2978 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2505 + <td><?php echo esc_html($intent->intent_label); ?></td>
2506 + <td class="mxchat-content-cell">
2507 + <?php echo esc_html($intent->phrases); ?>
2979 2508 </td>
2509 + <td><?php echo esc_html($callback_label); ?></td>
2510 + <td>
2511 + <form method="post"
2512 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2513 + class="mxchat-threshold-form">
2514 + <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2515 + <input type="hidden" name="action"
2516 + value="mxchat_update_intent_threshold">
2517 + <input type="hidden" name="intent_id"
2518 + value="<?php echo esc_attr($intent->id); ?>">
2519 + <div class="mxchat-slider-group">
2520 + <input type="range"
2521 + name="intent_threshold"
2522 + id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2523 + min="70"
2524 + max="95"
2525 + value="<?php echo esc_attr($threshold_value); ?>"
2526 + class="mxchat-intent-slider"
2527 + oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2528 + <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2529 + class="mxchat-intent-output">
2530 + <?php echo esc_html($threshold_value); ?>%
2531 + </output>
2532 + </div>
2533 + <button type="submit" class="mxchat-button-icon">
2534 + <span class="dashicons dashicons-saved"></span>
2535 + </button>
2536 + </form>
2537 + </td>
2538 + <td class="mxchat-actions-cell">
2539 + <button type="button"
2540 + class="mxchat-button-icon mxchat-edit-button"
2541 + data-intent-id="<?php echo esc_attr($intent->id); ?>"
2542 + data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2543 + <span class="dashicons dashicons-edit"></span>
2544 + </button>
2545 + <form method="post"
2546 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2547 + class="mxchat-delete-form"
2548 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2549 + <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2550 + <input type="hidden" name="action" value="mxchat_delete_intent">
2551 + <input type="hidden" name="intent_id"
2552 + value="<?php echo esc_attr($intent->id); ?>">
2553 + <button type="submit" class="mxchat-button-icon">
2554 + <span class="dashicons dashicons-trash"></span>
2555 + </button>
2556 + </form>
2557 + </td>
2980 2558 </tr>
2981 - <?php endif; ?>
2982 - </tbody>
2559 + <?php endforeach;
2560 + else : ?>
2561 + <tr>
2562 + <td colspan="5" class="mxchat-no-records">
2563 + <?php esc_html_e('No intents found.', 'mxchat'); ?>
2564 + </td>
2565 + </tr>
2566 + <?php endif; ?>
2567 + </tbody>
2983 2568 </table>
2984 2569 </div>
2985 2570
2986 2571 <?php if ($total_pages > 1) : ?>
@@ -3006,26 +2591,26 @@
3006 2591 <span class="mxchat-modal-close">&times;</span>
3007 2592 <div class="mxchat-card-header">
3008 2593 <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
3009 2594 </div>
3010 -
3011 - <form id="mxchat-edit-intent-form" method="post"
2595 +
2596 + <form id="mxchat-edit-intent-form" method="post"
3012 2597 action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3013 2598 <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
3014 2599 <input type="hidden" name="action" value="mxchat_edit_intent">
3015 2600 <input type="hidden" name="intent_id" id="edit_intent_id">
3016 -
2601 +
3017 2602 <div class="mxchat-form-group">
3018 2603 <label for="edit_phrases">
3019 2604 <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
3020 2605 </label>
3021 - <textarea name="phrases"
3022 - id="edit_phrases"
3023 - rows="5"
3024 - class="mxchat-intent-textarea"
2606 + <textarea name="phrases"
2607 + id="edit_phrases"
2608 + rows="5"
2609 + class="mxchat-intent-textarea"
3025 2610 required></textarea>
3026 2611 </div>
3027 -
2612 +
3028 2613 <div class="mxchat-modal-actions">
3029 2614 <button type="submit" class="mxchat-button-primary">
3030 2615 <?php esc_html_e('Update Phrases', 'mxchat'); ?>
3031 2616 </button>
@@ -3242,8 +2827,18 @@
3242 2827 'label' => __('Email Capture', 'mxchat'),
3243 2828 'pro_only' => false,
3244 2829 'group' => __('Customer Engagement', 'mxchat'),
3245 2830 ],
2831 + 'mxchat_handle_product_inquiry' => [
2832 + 'label' => __('Show Product Card', 'mxchat'),
2833 + 'pro_only' => true,
2834 + 'group' => __('WooCommerce Features', 'mxchat'),
2835 + ],
2836 + 'mxchat_handle_order_history' => [
2837 + 'label' => __('Order History', 'mxchat'),
2838 + 'pro_only' => true,
2839 + 'group' => __('WooCommerce Features', 'mxchat'),
2840 + ],
3246 2841 'mxchat_generate_image' => [
3247 2842 'label' => __('Generate Image', 'mxchat'),
3248 2843 'pro_only' => true,
3249 2844 'group' => __('Other Features', 'mxchat'),
@@ -3257,13 +2852,28 @@
3257 2852 'label' => __('Brave Image Search', 'mxchat'),
3258 2853 'pro_only' => false,
3259 2854 'group' => __('Search Features', 'mxchat'),
3260 2855 ],
2856 + 'mxchat_handle_add_to_cart_intent' => [
2857 + 'label' => __('Add to Cart', 'mxchat'),
2858 + 'pro_only' => true,
2859 + 'group' => __('WooCommerce Features', 'mxchat'),
2860 + ],
2861 + 'mxchat_handle_checkout_intent' => [
2862 + 'label' => __('Proceed to Checkout', 'mxchat'),
2863 + 'pro_only' => true,
2864 + 'group' => __('WooCommerce Features', 'mxchat'),
2865 + ],
3261 2866 'mxchat_handle_pdf_discussion' => [
3262 2867 'label' => __('Chat with PDF', 'mxchat'),
3263 2868 'pro_only' => true,
3264 2869 'group' => __('Other Features', 'mxchat'),
3265 2870 ],
2871 + 'mxchat_handle_product_recommendations' => [
2872 + 'label' => __('Product Recommendations', 'mxchat'),
2873 + 'pro_only' => true,
2874 + 'group' => __('WooCommerce Features', 'mxchat'),
2875 + ],
3266 2876 'mxchat_live_agent_handover' => [
3267 2877 'label' => __('Live Agent', 'mxchat'),
3268 2878 'pro_only' => true,
3269 2879 'group' => __('Customer Engagement', 'mxchat'),
@@ -3274,11 +2884,8 @@
3274 2884 'group' => __('Customer Engagement', 'mxchat'),
3275 2885 ],
3276 2886 ];
3277 2887
3278 - // Allow other plugins to add their callbacks
3279 - $callbacks = apply_filters('mxchat_available_callbacks', $callbacks);
3280 -
3281 2888 // Return grouped structure if requested
3282 2889 if ($grouped) {
3283 2890 $grouped_callbacks = [];
3284 2891 foreach ($callbacks as $key => $data) {
@@ -3289,12 +2896,14 @@
3289 2896 ];
3290 2897 }
3291 2898 return $grouped_callbacks;
3292 2899 }
2900 +
3293 2901 return $callbacks;
3294 2902 }
3295 2903
3296 2904
2905 +
3297 2906 private function mxchat_average_vectors($vectors) {
3298 2907 $vector_length = count($vectors[0]);
3299 2908 $sum_vector = array_fill(0, $vector_length, 0);
3300 2909
@@ -3412,44 +3021,25 @@
3412 3021 'mxchat-chatbot',
3413 3022 'mxchat_chatbot_section'
3414 3023 );
3415 3024
3416 - add_settings_field(
3417 - 'voyage_api_key',
3418 - esc_html__('Voyage AI API Key', 'mxchat'),
3419 - array($this, 'voyage_api_key_callback'),
3420 - 'mxchat-chatbot',
3421 - 'mxchat_chatbot_section'
3422 - );
3423 -
3424 -
3425 3025 add_settings_field(
3426 - 'model',
3427 - esc_html__('Chat Model', 'mxchat'),
3428 - array($this, 'mxchat_model_callback'),
3026 + 'system_prompt_instructions',
3027 + esc_html__('AI Instructions (Behavior)', 'mxchat'),
3028 + array($this, 'system_prompt_instructions_callback'),
3429 3029 'mxchat-chatbot',
3430 3030 'mxchat_chatbot_section'
3431 3031 );
3432 3032
3433 - // Add the settings field
3434 3033 add_settings_field(
3435 - 'embedding_model',
3436 - esc_html__('Embedding Model', 'mxchat'),
3437 - array($this, 'embedding_model_callback'),
3034 + 'model',
3035 + esc_html__('Model', 'mxchat'),
3036 + array($this, 'mxchat_model_callback'),
3438 3037 'mxchat-chatbot',
3439 3038 'mxchat_chatbot_section'
3440 3039 );
3441 3040
3442 3041 add_settings_field(
3443 - 'system_prompt_instructions',
3444 - esc_html__('AI Instructions (Behavior)', 'mxchat'),
3445 - array($this, 'system_prompt_instructions_callback'),
3446 - 'mxchat-chatbot',
3447 - 'mxchat_chatbot_section'
3448 - );
3449 -
3450 -
3451 - add_settings_field(
3452 3042 'top_bar_title',
3453 3043 esc_html__('Top Bar Title', 'mxchat'),
3454 3044 array($this, 'mxchat_top_bar_title_callback'),
3455 3045 'mxchat-chatbot',
@@ -3561,9 +3151,9 @@
3561 3151 );
3562 3152
3563 3153 add_settings_field(
3564 3154 'popular_question_1',
3565 - esc_html__('Quick Question 1', 'mxchat'),
3155 + esc_html__('Popular Question 1', 'mxchat'),
3566 3156 array($this, 'mxchat_popular_question_1_callback'),
3567 3157 'mxchat-chatbot',
3568 3158 'mxchat_chatbot_section'
3569 3159 );
@@ -3569,9 +3159,9 @@
3569 3159 );
3570 3160
3571 3161 add_settings_field(
3572 3162 'popular_question_2',
3573 - esc_html__('Quick Question 2', 'mxchat'),
3163 + esc_html__('Popular Question 2', 'mxchat'),
3574 3164 array($this, 'mxchat_popular_question_2_callback'),
3575 3165 'mxchat-chatbot',
3576 3166 'mxchat_chatbot_section'
3577 3167 );
@@ -3577,9 +3167,9 @@
3577 3167 );
3578 3168
3579 3169 add_settings_field(
3580 3170 'popular_question_3',
3581 - esc_html__('Quick Question 3', 'mxchat'),
3171 + esc_html__('Popular Question 3', 'mxchat'),
3582 3172 array($this, 'mxchat_popular_question_3_callback'),
3583 3173 'mxchat-chatbot',
3584 3174 'mxchat_chatbot_section'
3585 3175 );
@@ -3585,14 +3175,21 @@
3585 3175 );
3586 3176
3587 3177 add_settings_field(
3588 3178 'additional_popular_questions',
3589 - esc_html__('Additional Quick Questions', 'mxchat'),
3179 + esc_html__('Additional Popular Questions', 'mxchat'),
3590 3180 array($this, 'mxchat_additional_popular_questions_callback'),
3591 3181 'mxchat-chatbot',
3592 3182 'mxchat_chatbot_section'
3593 3183 );
3594 3184
3185 + // WooCommerce Settings Section
3186 + add_settings_section(
3187 + 'mxchat_woocommerce_section',
3188 + esc_html__('WooCommerce Settings', 'mxchat'),
3189 + null,
3190 + 'mxchat-embed'
3191 + );
3595 3192
3596 3193 // Loops Settings Section
3597 3194 add_settings_section(
3598 3195 'mxchat_loops_section',
@@ -3600,8 +3197,33 @@
3600 3197 null,
3601 3198 'mxchat-embed'
3602 3199 );
3603 3200
3201 + // WooCommerce Settings Fields
3202 + add_settings_field(
3203 + 'enable_woocommerce_integration',
3204 + esc_html__('Automatically Embed Products', 'mxchat'),
3205 + array($this, 'mxchat_enable_woocommerce_integration_callback'),
3206 + 'mxchat-embed',
3207 + 'mxchat_woocommerce_section'
3208 + );
3209 +
3210 + add_settings_field(
3211 + 'woocommerce_consumer_key',
3212 + esc_html__('WooCommerce Consumer Key', 'mxchat'),
3213 + array($this, 'mxchat_woocommerce_consumer_key_callback'),
3214 + 'mxchat-embed',
3215 + 'mxchat_woocommerce_section'
3216 + );
3217 +
3218 + add_settings_field(
3219 + 'woocommerce_consumer_secret',
3220 + esc_html__('WooCommerce Consumer Secret', 'mxchat'),
3221 + array($this, 'mxchat_woocommerce_consumer_secret_callback'),
3222 + 'mxchat-embed',
3223 + 'mxchat_woocommerce_section'
3224 + );
3225 +
3604 3226 // Loops Settings Fields
3605 3227 add_settings_field(
3606 3228 'loops_api_key',
3607 3229 esc_html__('Loops API Key', 'mxchat'),
@@ -3796,11 +3418,162 @@
3796 3418 'mxchat-embed',
3797 3419 'mxchat_live_agent_section'
3798 3420 );
3799 3421
3422 + // Theme Settings Section
3423 + add_settings_section(
3424 + 'mxchat_theme_section',
3425 + esc_html__('Theme Settings', 'mxchat'),
3426 + null,
3427 + 'mxchat-theme'
3428 + );
3800 3429
3430 + add_settings_field(
3431 + 'close_button_color',
3432 + esc_html__('Close Button & Title Color', 'mxchat'),
3433 + array($this, 'mxchat_close_button_color_callback'),
3434 + 'mxchat-theme',
3435 + 'mxchat_theme_section'
3436 + );
3801 3437
3438 + add_settings_field(
3439 + 'chatbot_bg_color',
3440 + esc_html__('Chatbot Background Color', 'mxchat'),
3441 + array($this, 'mxchat_chatbot_bg_color_callback'),
3442 + 'mxchat-theme',
3443 + 'mxchat_theme_section'
3444 + );
3802 3445
3446 + add_settings_field(
3447 + 'user_message_bg_color',
3448 + esc_html__('User Message Background Color', 'mxchat'),
3449 + array($this, 'mxchat_user_message_bg_color_callback'),
3450 + 'mxchat-theme',
3451 + 'mxchat_theme_section'
3452 + );
3453 +
3454 + add_settings_field(
3455 + 'user_message_font_color',
3456 + esc_html__('User Message Font Color', 'mxchat'),
3457 + array($this, 'mxchat_user_message_font_color_callback'),
3458 + 'mxchat-theme',
3459 + 'mxchat_theme_section'
3460 + );
3461 +
3462 + add_settings_field(
3463 + 'bot_message_bg_color',
3464 + esc_html__('Bot Message Background Color', 'mxchat'),
3465 + array($this, 'mxchat_bot_message_bg_color_callback'),
3466 + 'mxchat-theme',
3467 + 'mxchat_theme_section'
3468 + );
3469 +
3470 + add_settings_field(
3471 + 'bot_message_font_color',
3472 + esc_html__('Bot Message Font Color', 'mxchat'),
3473 + array($this, 'mxchat_bot_message_font_color_callback'),
3474 + 'mxchat-theme',
3475 + 'mxchat_theme_section'
3476 + );
3477 +
3478 + add_settings_field(
3479 + 'top_bar_bg_color',
3480 + esc_html__('Top Bar Background Color', 'mxchat'),
3481 + array($this, 'mxchat_top_bar_bg_color_callback'),
3482 + 'mxchat-theme',
3483 + 'mxchat_theme_section'
3484 + );
3485 +
3486 + add_settings_field(
3487 + 'send_button_font_color',
3488 + esc_html__('Send Button Color', 'mxchat'),
3489 + array($this, 'mxchat_send_button_font_color_callback'),
3490 + 'mxchat-theme',
3491 + 'mxchat_theme_section'
3492 + );
3493 +
3494 + add_settings_field(
3495 + 'chat_input_font_color',
3496 + esc_html__('Chat Input Font Color', 'mxchat'),
3497 + array($this, 'mxchat_chat_input_font_color_callback'),
3498 + 'mxchat-theme',
3499 + 'mxchat_theme_section'
3500 + );
3501 +
3502 + add_settings_field(
3503 + 'chatbot_background_color',
3504 + esc_html__('Floating Widget Background Color', 'mxchat'),
3505 + array($this, 'mxchat_chatbot_background_color_callback'),
3506 + 'mxchat-theme',
3507 + 'mxchat_theme_section'
3508 + );
3509 +
3510 + add_settings_field(
3511 + 'icon_color',
3512 + esc_html__('Chatbot Icon Color', 'mxchat'),
3513 + array($this, 'mxchat_icon_color_callback'),
3514 + 'mxchat-theme',
3515 + 'mxchat_theme_section'
3516 + );
3517 +
3518 + add_settings_field(
3519 + 'custom_icon',
3520 + esc_html__('Custom Chatbot Icon (PNG)', 'mxchat'),
3521 + array($this, 'mxchat_custom_icon_callback'),
3522 + 'mxchat-theme',
3523 + 'mxchat_theme_section'
3524 + );
3525 +
3526 + add_settings_field(
3527 + 'title_icon',
3528 + esc_html__('Title Bar Icon (PNG)', 'mxchat'),
3529 + array($this, 'mxchat_title_icon_callback'),
3530 + 'mxchat-theme',
3531 + 'mxchat_theme_section'
3532 + );
3533 +
3534 + add_settings_field(
3535 + 'live_agent_message_bg_color',
3536 + esc_html__('Live Agent Background Color', 'mxchat'),
3537 + array($this, 'mxchat_live_agent_message_bg_color_callback'),
3538 + 'mxchat-theme',
3539 + 'mxchat_theme_section'
3540 + );
3541 +
3542 + add_settings_field(
3543 + 'live_agent_message_font_color',
3544 + esc_html__('Live Agent Font Color', 'mxchat'),
3545 + array($this, 'mxchat_live_agent_message_font_color_callback'),
3546 + 'mxchat-theme',
3547 + 'mxchat_theme_section'
3548 + );
3549 +
3550 + // Mode Indicator Background Color
3551 + add_settings_field(
3552 + 'mode_indicator_bg_color',
3553 + esc_html__('Mode Indicator Background Color', 'mxchat'),
3554 + array($this, 'mxchat_mode_indicator_bg_color_callback'),
3555 + 'mxchat-theme',
3556 + 'mxchat_theme_section'
3557 + );
3558 +
3559 + // Mode Indicator Font Color
3560 + add_settings_field(
3561 + 'mode_indicator_font_color',
3562 + esc_html__('Mode Indicator Font Color', 'mxchat'),
3563 + array($this, 'mxchat_mode_indicator_font_color_callback'),
3564 + 'mxchat-theme',
3565 + 'mxchat_theme_section'
3566 + );
3567 +
3568 + add_settings_field(
3569 + 'toolbar_icon_color',
3570 + esc_html__('Toolbar Icon Color', 'mxchat'),
3571 + array($this, 'mxchat_toolbar_icon_color_callback'),
3572 + 'mxchat-theme',
3573 + 'mxchat_theme_section'
3574 + );
3575 +
3803 3576 // General Settings Section
3804 3577 add_settings_section(
3805 3578 'mxchat_general_section',
3806 3579 esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
@@ -3835,9 +3608,9 @@
3835 3608 }
3836 3609
3837 3610 /**
3838 3611 * Sanitize all prompts options
3839 - *
3612 + *
3840 3613 * @param array $input The unsanitized options array
3841 3614 * @return array The sanitized options array
3842 3615 */
3843 3616 public function sanitize_prompts_options($input) {
@@ -3842,16 +3615,16 @@
3842 3615 */
3843 3616 public function sanitize_prompts_options($input) {
3844 3617 // Log the incoming input.
3845 3618 //error_log('Sanitizing inputs: ' . print_r($input, true));
3846 -
3619 +
3847 3620 $sanitized = array();
3848 -
3621 +
3849 3622 // Boolean options
3850 3623 $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0;
3851 3624 $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0;
3852 3625 $sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0;
3853 -
3626 +
3854 3627 // API Key: if less than 32 characters, flag as invalid.
3855 3628 $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
3856 3629 if (!empty($api_key) && strlen($api_key) < 32) {
3857 3630 add_settings_error(
@@ -3863,13 +3636,13 @@
3863 3636 $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? '';
3864 3637 } else {
3865 3638 $sanitized['mxchat_pinecone_api_key'] = $api_key;
3866 3639 }
3867 -
3640 +
3868 3641 // Environment and Index Name
3869 3642 $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
3870 3643 $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
3871 -
3644 +
3872 3645 // Host: Remove protocol and validate format.
3873 3646 $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
3874 3647 $host = preg_replace('#^https?://#', '', $host);
3875 3648 //error_log('Host after removing protocol: ' . $host);
@@ -3887,11 +3660,11 @@
3887 3660 }
3888 3661 } else {
3889 3662 $sanitized['mxchat_pinecone_host'] = '';
3890 3663 }
3891 -
3664 +
3892 3665 //error_log('Final sanitized array: ' . print_r($sanitized, true));
3893 -
3666 +
3894 3667 return $sanitized;
3895 3668 }
3896 3669
3897 3670
@@ -4047,8 +3820,52 @@
4047 3820 echo '</div>';
4048 3821 }
4049 3822
4050 3823
3824 +public function mxchat_enable_woocommerce_integration_callback() {
3825 + // Load from mxchat_options array
3826 + $options = get_option('mxchat_options', []);
3827 +
3828 + // Get WooCommerce integration status with backwards compatibility
3829 + $woo_integration = isset($options['enable_woocommerce_integration'])
3830 + ? $options['enable_woocommerce_integration']
3831 + : '';
3832 +
3833 + // Support both old ('1') and new ('on') values
3834 + $checked = ($woo_integration === 'on' || $woo_integration === '1') ? 'checked' : '';
3835 +
3836 + // Check if the feature is activated (paid feature)
3837 + $disabled = $this->is_activated ? '' : 'disabled';
3838 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3839 +
3840 + echo '<div class="' . esc_attr($class) . '">';
3841 +
3842 + echo '<label class="toggle-switch">';
3843 + echo sprintf(
3844 + '<input type="checkbox" id="enable_woocommerce_integration" name="enable_woocommerce_integration" value="on" %s %s />',
3845 + esc_attr($checked),
3846 + esc_attr($disabled)
3847 + );
3848 + echo '<span class="slider"></span>';
3849 + echo '</label>';
3850 +
3851 + 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>';
3852 +
3853 + // Pro feature overlay for non-activated users
3854 + if (!$this->is_activated) {
3855 + echo '<div class="pro-feature-overlay">';
3856 + echo '<a href="https://mxchat.ai/" target="_blank">';
3857 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
3858 + echo '</a>';
3859 + echo '</div>';
3860 + }
3861 +
3862 + echo '</div>';
3863 +}
3864 +
3865 +
3866 +
3867 +
4051 3868 private function mxchat_add_option_field($id, $title, $callback = '') {
4052 3869 add_settings_field(
4053 3870 $id,
4054 3871 __($title, 'mxchat'),
@@ -4065,8 +3882,9 @@
4065 3882
4066 3883 // Notice we changed the name to "api_key" (no array notation).
4067 3884 echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
4068 3885 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3886 + 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>';
4069 3887 }
4070 3888 public function xai_api_key_callback() {
4071 3889 // Check if the feature is activated (paid feature)
4072 3890 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
@@ -4094,15 +3912,15 @@
4094 3912 echo '</div>';
4095 3913 }
4096 3914 public function claude_api_key_callback() {
4097 3915 // Check if the feature is activated (paid feature)
4098 - $disabled = $this->is_activated ? '' : 'disabled';
4099 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3916 + $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3917 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
4100 3918
4101 - // Retrieve the Claude API key value directly like the others
3919 + // Retrieve the Claude API key value
4102 3920 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
4103 3921
4104 - // Use direct name field like the other working API keys
3922 + // Render the input field for the Claude API key
4105 3923 echo '<div class="' . esc_attr($class) . '">';
4106 3924 printf(
4107 3925 '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
4108 3926 $claudeApiKey,
@@ -4109,13 +3927,15 @@
4109 3927 $disabled
4110 3928 );
4111 3929 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4112 3930
3931 + // If the feature is not activated, show the overlay with a "Pro Only" message
4113 3932 if (!$this->is_activated) {
4114 3933 echo '<div class="pro-feature-overlay">';
4115 3934 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>';
4116 3935 echo '</div>';
4117 3936 }
3937 +
4118 3938 echo '</div>';
4119 3939 }
4120 3940 public function deepseek_api_key_callback() {
4121 3941 // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
@@ -4126,11 +3946,79 @@
4126 3946 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4127 3947 }
4128 3948
4129 3949
3950 +public function mxchat_woocommerce_consumer_key_callback() {
3951 + // Load the entire 'mxchat_options' array
3952 + $all_options = get_option('mxchat_options', []);
4130 3953
3954 + // Retrieve the WooCommerce consumer key or set a default
3955 + $consumer_key = isset($all_options['woocommerce_consumer_key']) ? $all_options['woocommerce_consumer_key'] : '';
4131 3956
3957 + // Check if the feature is activated (paid feature)
3958 + $disabled = $this->is_activated ? '' : 'disabled';
3959 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4132 3960
3961 + echo '<div class="' . esc_attr($class) . '">';
3962 +
3963 + // Render the input field
3964 + printf(
3965 + '<input type="text" id="woocommerce_consumer_key" name="woocommerce_consumer_key" value="%s" class="regular-text" %s />',
3966 + esc_attr($consumer_key),
3967 + esc_attr($disabled)
3968 + );
3969 +
3970 + // Description for the input field
3971 + echo '<p class="description">' . esc_html__('Enter your WooCommerce consumer key for integration.', 'mxchat') . '</p>';
3972 +
3973 + // Pro feature overlay for non-activated users
3974 + if (!$this->is_activated) {
3975 + echo '<div class="pro-feature-overlay">';
3976 + 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>';
3977 + echo '</div>';
3978 + }
3979 +
3980 + echo '</div>';
3981 +}
3982 +
3983 +public function mxchat_woocommerce_consumer_secret_callback() {
3984 + // Get value with fallback
3985 + $consumer_secret = isset($this->options['woocommerce_consumer_secret'])
3986 + ? esc_attr($this->options['woocommerce_consumer_secret'])
3987 + : '';
3988 +
3989 + // Check if the feature is activated (paid feature)
3990 + $disabled = $this->is_activated ? '' : 'disabled';
3991 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3992 +
3993 + echo '<div class="' . esc_attr($class) . '">';
3994 +
3995 + // Output the password input
3996 + echo sprintf(
3997 + '<input type="password"
3998 + id="woocommerce_consumer_secret"
3999 + name="woocommerce_consumer_secret"
4000 + value="%s"
4001 + class="regular-text"
4002 + %s />',
4003 + $consumer_secret,
4004 + esc_attr($disabled)
4005 + );
4006 +
4007 + // Add show/hide button
4008 + echo '<button type="button" id="toggleWooCommerceSecretVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4009 +
4010 + // Pro feature overlay
4011 + if (!$this->is_activated) {
4012 + echo '<div class="pro-feature-overlay">';
4013 + echo '<a href="https://mxchat.ai/" target="_blank">';
4014 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4015 + echo '</a>';
4016 + echo '</div>';
4017 + }
4018 +
4019 + echo '</div>';
4020 +}
4133 4021 public function mxchat_loops_api_key_callback() {
4134 4022 // Support both old and new format
4135 4023 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4136 4024
@@ -4291,46 +4179,13 @@
4291 4179 // Close the select dropdown
4292 4180 echo '</select>';
4293 4181
4294 4182 // Add a description below the dropdown
4295 - echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
4183 + echo '<p class="description">' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
4296 4184 }
4297 4185
4298 -public function voyage_api_key_callback() {
4299 - $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4300 - echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" />';
4301 - echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4302 -}
4303 4186
4304 -// Callback function for embedding model selection
4305 -public function embedding_model_callback() {
4306 - $models = array(
4307 - esc_html__('OpenAI Embeddings', 'mxchat') => array(
4308 - 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
4309 - 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Original)', 'mxchat'),
4310 - 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
4311 - ),
4312 - esc_html__('Voyage AI Embeddings', 'mxchat') => array(
4313 - 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
4314 - )
4315 - );
4316 4187
4317 - $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
4318 -
4319 - echo '<select id="embedding_model" name="embedding_model">';
4320 - foreach ($models as $group_label => $group_models) {
4321 - echo '<optgroup label="' . esc_attr($group_label) . '">';
4322 - foreach ($group_models as $model_value => $model_label) {
4323 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4324 - }
4325 - echo '</optgroup>';
4326 - }
4327 - echo '</select>';
4328 -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>';
4329 -
4330 -}
4331 -
4332 -
4333 4188 public function mxchat_top_bar_title_callback() {
4334 4189 // Retrieve the current value of the top bar title from saved options
4335 4190 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4336 4191
@@ -4443,8 +4298,91 @@
4443 4298 }
4444 4299
4445 4300
4446 4301
4302 +
4303 +
4304 +public function mxchat_close_button_color_callback() {
4305 + $disabled = $this->is_activated ? '' : 'disabled';
4306 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4307 +
4308 + echo '<div class="' . esc_attr($class) . '">';
4309 + echo sprintf(
4310 + '<input type="text"
4311 + id="close_button_color"
4312 + name="close_button_color"
4313 + value="%s"
4314 + class="my-color-field"
4315 + data-default-color="#4a4a4a"
4316 + %s />',
4317 + isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '#4a4a4a',
4318 + esc_attr($disabled)
4319 + );
4320 +
4321 + if (!$this->is_activated) {
4322 + echo '<div class="pro-feature-overlay">';
4323 + echo '<a href="https://mxchat.ai/" target="_blank">';
4324 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4325 + echo '</a>';
4326 + echo '</div>';
4327 + }
4328 + echo '</div>';
4329 +}
4330 +
4331 +public function mxchat_chatbot_bg_color_callback() {
4332 + $disabled = $this->is_activated ? '' : 'disabled';
4333 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4334 +
4335 + echo '<div class="' . esc_attr($class) . '">';
4336 + echo sprintf(
4337 + '<input type="text"
4338 + id="chatbot_bg_color"
4339 + name="chatbot_bg_color"
4340 + value="%s"
4341 + class="my-color-field"
4342 + data-default-color="#f9f9f9"
4343 + %s />',
4344 + isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '#f9f9f9',
4345 + esc_attr($disabled)
4346 + );
4347 +
4348 + if (!$this->is_activated) {
4349 + echo '<div class="pro-feature-overlay">';
4350 + echo '<a href="https://mxchat.ai/" target="_blank">';
4351 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4352 + echo '</a>';
4353 + echo '</div>';
4354 + }
4355 + echo '</div>';
4356 +}
4357 +
4358 +public function mxchat_user_message_bg_color_callback() {
4359 + $disabled = $this->is_activated ? '' : 'disabled';
4360 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4361 +
4362 + echo '<div class="' . esc_attr($class) . '">';
4363 + echo sprintf(
4364 + '<input type="text"
4365 + id="user_message_bg_color"
4366 + name="user_message_bg_color"
4367 + value="%s"
4368 + class="my-color-field"
4369 + data-default-color="#0078d7"
4370 + %s />',
4371 + isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '#0078d7',
4372 + esc_attr($disabled)
4373 + );
4374 +
4375 + if (!$this->is_activated) {
4376 + echo '<div class="pro-feature-overlay">';
4377 + echo '<a href="https://mxchat.ai/" target="_blank">';
4378 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4379 + echo '</a>';
4380 + echo '</div>';
4381 + }
4382 + echo '</div>';
4383 +}
4384 +
4447 4385 public function mxchat_user_message_font_color_callback() {
4448 4386 $disabled = $this->is_activated ? '' : 'disabled';
4449 4387 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4450 4388
@@ -4931,20 +4869,39 @@
4931 4869 // Get complianz toggle value with fallback
4932 4870 $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
4933 4871 $checked = ($complianz_toggle === 'on') ? 'checked' : '';
4934 4872
4873 + // Check if the plugin is activated (paid feature)
4874 + $disabled = $this->is_activated ? '' : 'disabled';
4875 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4876 +
4877 + echo '<div class="' . esc_attr($class) . '">';
4878 +
4935 4879 // Output the toggle switch
4936 4880 echo '<label class="toggle-switch">';
4937 4881 echo sprintf(
4938 - '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
4939 - esc_attr($checked)
4882 + '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s %s />',
4883 + esc_attr($checked),
4884 + esc_attr($disabled)
4940 4885 );
4941 4886 echo '<span class="slider"></span>';
4942 4887 echo '</label>';
4943 4888
4944 4889 echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
4890 +
4891 + // If the feature is not activated, show the Pro feature overlay
4892 + if (!$this->is_activated) {
4893 + echo '<div class="pro-feature-overlay">';
4894 + echo '<a href="https://mxchat.ai/" target="_blank">';
4895 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4896 + echo '</a>';
4897 + echo '</div>';
4898 + }
4899 +
4900 + echo '</div>';
4945 4901 }
4946 4902
4903 +
4947 4904 public function mxchat_link_target_toggle_callback() {
4948 4905 // Load from mxchat_options array
4949 4906 $options = get_option('mxchat_options', []);
4950 4907
@@ -4970,20 +4927,39 @@
4970 4927 // Get chat persistence toggle value with fallback
4971 4928 $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
4972 4929 $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
4973 4930
4931 + // Check if the plugin is activated (paid feature)
4932 + $disabled = $this->is_activated ? '' : 'disabled';
4933 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4934 +
4935 + echo '<div class="' . esc_attr($class) . '">';
4936 +
4974 4937 // Output the toggle switch
4975 4938 echo '<label class="toggle-switch">';
4976 4939 echo sprintf(
4977 - '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
4978 - esc_attr($checked)
4940 + '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s %s />',
4941 + esc_attr($checked),
4942 + esc_attr($disabled)
4979 4943 );
4980 4944 echo '<span class="slider"></span>';
4981 4945 echo '</label>';
4982 4946
4983 4947 echo '<p class="description">' . esc_html__('Enable to keep chat history when users navigate tabs or return to the site within 24 hours.', 'mxchat') . '</p>';
4948 +
4949 + // If not activated, show Pro feature overlay
4950 + if (!$this->is_activated) {
4951 + echo '<div class="pro-feature-overlay">';
4952 + echo '<a href="https://mxchat.ai/" target="_blank">';
4953 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4954 + echo '</a>';
4955 + echo '</div>';
4956 + }
4957 +
4958 + echo '</div>';
4984 4959 }
4985 4960
4961 +
4986 4962 public function mxchat_popular_question_1_callback() {
4987 4963 // Load the full plugin options array
4988 4964 $all_options = get_option('mxchat_options', []);
4989 4965
@@ -4993,13 +4969,13 @@
4993 4969 // Render the input field
4994 4970 printf(
4995 4971 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4996 4972 esc_attr($popular_question_1),
4997 - esc_attr__('Enter Quick Question 1', 'mxchat')
4973 + esc_attr__('Enter Popular Question 1', 'mxchat')
4998 4974 );
4999 4975
5000 4976 // Add a description for the field
5001 - echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
4977 + echo '<p class="description">' . esc_html__('This will be the first popular question in the chatbot.', 'mxchat') . '</p>';
5002 4978 }
5003 4979
5004 4980
5005 4981 public function mxchat_popular_question_2_callback() {
@@ -5008,38 +4984,72 @@
5008 4984
5009 4985 // Retrieve the specific option for popular_question_2
5010 4986 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
5011 4987
4988 + // Check if the plugin is activated (paid feature)
4989 + $disabled = $this->is_activated ? '' : 'disabled';
4990 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4991 +
4992 + echo '<div class="' . esc_attr($class) . '">';
4993 +
5012 4994 // Render the input field
5013 4995 printf(
5014 - '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4996 + '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" %s />',
5015 4997 esc_attr($popular_question_2),
5016 - esc_attr__('Enter Quick Question 2', 'mxchat')
4998 + esc_attr__('Enter Popular Question 2', 'mxchat'),
4999 + esc_attr($disabled)
5017 5000 );
5018 5001
5019 5002 // Add a description for the field
5020 - echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
5003 + echo '<p class="description">' . esc_html__('This will be the second popular question in the chatbot.', 'mxchat') . '</p>';
5004 +
5005 + // If not activated, show Pro feature overlay
5006 + if (!$this->is_activated) {
5007 + echo '<div class="pro-feature-overlay">';
5008 + 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>';
5009 + echo '</div>';
5010 + }
5011 +
5012 + echo '</div>';
5021 5013 }
5022 5014
5023 5015
5016 +
5024 5017 public function mxchat_popular_question_3_callback() {
5025 - // Load the full plugin options array
5026 - $all_options = get_option('mxchat_options', []);
5018 + // Load the full plugin options array
5019 + $all_options = get_option('mxchat_options', []);
5027 5020
5028 - // Retrieve the specific option for popular_question_3
5029 - $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
5021 + // Retrieve the specific option for popular_question_3
5022 + $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
5030 5023
5031 - // Render the input field
5032 - printf(
5033 - '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
5034 - esc_attr($popular_question_3),
5035 - esc_attr(__('Enter Quick Question 3', 'mxchat'))
5036 - );
5024 + // Check if the plugin is activated (paid feature)
5025 + $disabled = $this->is_activated ? '' : 'disabled';
5026 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5037 5027
5038 - // Add a description for the field
5039 - echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
5028 + echo '<div class="' . esc_attr($class) . '">';
5029 +
5030 + // Render the input field
5031 + printf(
5032 + '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" %s />',
5033 + esc_attr($popular_question_3),
5034 + esc_attr(__('Enter Popular Question 3', 'mxchat')),
5035 + esc_attr($disabled)
5036 + );
5037 +
5038 + // Add a description for the field
5039 + echo '<p class="description">' . esc_html__('This will be the third popular question in the chatbot.', 'mxchat') . '</p>';
5040 +
5041 + // If not activated, show Pro feature overlay
5042 + if (!$this->is_activated) {
5043 + echo '<div class="pro-feature-overlay">';
5044 + 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>';
5045 + echo '</div>';
5046 + }
5047 +
5048 + echo '</div>';
5040 5049 }
5041 5050
5051 +
5042 5052 public function mxchat_additional_popular_questions_callback() {
5043 5053 $options = get_option('mxchat_options', []);
5044 5054 $additional_questions = isset($options['additional_popular_questions'])
5045 5055 ? $options['additional_popular_questions']
@@ -5058,9 +5068,9 @@
5058 5068 <button type="button" class="button mxchat-remove-question"
5059 5069 aria-label="%s">%s</button>
5060 5070 </div>',
5061 5071 esc_attr($question),
5062 - esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
5072 + esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
5063 5073 $index,
5064 5074 esc_attr(__('Remove question', 'mxchat')),
5065 5075 esc_html__('Remove', 'mxchat')
5066 5076 );
@@ -5075,9 +5085,9 @@
5075 5085 data-question-index="0" />
5076 5086 <button type="button" class="button mxchat-remove-question"
5077 5087 aria-label="%s">%s</button>
5078 5088 </div>',
5079 - esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
5089 + esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
5080 5090 esc_attr(__('Remove question', 'mxchat')),
5081 5091 esc_html__('Remove', 'mxchat')
5082 5092 );
5083 5093 }
@@ -5086,9 +5096,9 @@
5086 5096 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5087 5097 esc_attr(__('Add question', 'mxchat')),
5088 5098 esc_html__('Add Question', 'mxchat')
5089 5099 );
5090 - echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5100 + echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
5091 5101 echo '</div>';
5092 5102 }
5093 5103
5094 5104 public function mxchat_brave_api_key_callback() {
@@ -5524,9 +5534,9 @@
5524 5534 public function mxchat_enqueue_admin_assets() {
5525 5535 wp_enqueue_style('wp-color-picker');
5526 5536
5527 5537 // Get the plugin version or file modification time for cache busting
5528 - $plugin_version = '2.0.6'; // Replace this with your plugin's version
5538 + $plugin_version = '2.0.0'; // Replace this with your plugin's version
5529 5539
5530 5540 // File paths
5531 5541 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5532 5542 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
@@ -5532,12 +5542,10 @@
5532 5542 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5533 5543 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5534 5544 $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5535 5545 $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5536 - $transcripts_css_path = plugin_dir_path(__FILE__) . '../css/chat-transcripts.css';
5537 5546 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
5538 5547
5539 -
5540 5548 // Check if files exist and get modification times
5541 5549 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5542 5550 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5543 5551 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
@@ -5542,12 +5550,10 @@
5542 5550 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5543 5551 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5544 5552 $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5545 5553 $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5546 - $transcripts_css_version = file_exists($transcripts_css_path) ? filemtime($transcripts_css_path) : $plugin_version;
5547 5554 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5548 5555
5549 -
5550 5556 // Enqueue scripts and styles with corrected paths
5551 5557 wp_enqueue_script(
5552 5558 'mxchat-color-picker',
5553 5559 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
@@ -5564,8 +5570,16 @@
5564 5570 true
5565 5571 );
5566 5572
5567 5573 wp_enqueue_script(
5574 + 'mxchat-transcripts-js',
5575 + plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5576 + array('jquery'),
5577 + $transcripts_js_version,
5578 + true
5579 + );
5580 +
5581 + wp_enqueue_script(
5568 5582 'mxchat-admin-js',
5569 5583 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5570 5584 array('jquery'),
5571 5585 $plugin_version,
@@ -5576,11 +5590,10 @@
5576 5590 'ajax_url' => admin_url('admin-ajax.php'),
5577 5591 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5578 5592 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5579 5593 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5580 - 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
5581 5594 ));
5582 -
5595 +
5583 5596 // Enqueue the admin CSS
5584 5597 wp_enqueue_style(
5585 5598 'mxchat-admin-css',
5586 5599 plugin_dir_url(__FILE__) . '../css/admin-style.css',
@@ -5586,26 +5599,9 @@
5586 5599 plugin_dir_url(__FILE__) . '../css/admin-style.css',
5587 5600 array(),
5588 5601 $admin_css_version
5589 5602 );
5590 -
5591 - if (isset($_GET['page']) && $_GET['page'] === 'mxchat-transcripts') {
5592 - wp_enqueue_style(
5593 - 'mxchat-chat-transcripts-css',
5594 - plugin_dir_url(__FILE__) . '../css/chat-transcripts.css',
5595 - array(),
5596 - $transcripts_css_version
5597 - );
5598 -
5599 - wp_enqueue_script(
5600 - 'mxchat-transcripts-js',
5601 - plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5602 - array('jquery'),
5603 - $transcripts_js_version,
5604 - true
5605 - );
5606 - }
5607 -
5603 +
5608 5604 wp_enqueue_style(
5609 5605 'mxchat-knowledge-css',
5610 5606 plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5611 5607 array(),
@@ -5610,10 +5606,10 @@
5610 5606 plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5611 5607 array(),
5612 5608 $knowledge_css_version
5613 5609 );
5614 -
5615 -
5610 +
5611 +
5616 5612 wp_enqueue_style(
5617 5613 'mxchat-intent-css',
5618 5614 plugin_dir_url(__FILE__) . '../css/intent-style.css',
5619 5615 array(),
@@ -5618,9 +5614,9 @@
5618 5614 plugin_dir_url(__FILE__) . '../css/intent-style.css',
5619 5615 array(),
5620 5616 $intent_css_version
5621 5617 );
5622 -
5618 +
5623 5619 // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5624 5620 wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5625 5621 'ajax_url' => admin_url( 'admin-ajax.php' ),
5626 5622 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
@@ -5778,25 +5774,8 @@
5778 5774 if (isset($input['pre_chat_message'])) {
5779 5775 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5780 5776 }
5781 5777
5782 - if (isset($input['voyage_api_key'])) {
5783 - $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
5784 - }
5785 -
5786 - // Add to your sanitize function
5787 - if (isset($input['embedding_model'])) {
5788 - $allowed_models = array(
5789 - 'text-embedding-ada-002',
5790 - 'text-embedding-3-small',
5791 - 'text-embedding-3-large',
5792 - 'voyage-3-large'
5793 - );
5794 - if (in_array($input['embedding_model'], $allowed_models)) {
5795 - $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5796 - }
5797 - }
5798 -
5799 5778 if (isset($input['model'])) {
5800 5779 $allowed_models = array(
5801 5780 'grok-beta',
5802 5781 'grok-2',
@@ -5815,8 +5794,9 @@
5815 5794 $new_input['model'] = sanitize_text_field($input['model']);
5816 5795 }
5817 5796 }
5818 5797
5798 + // Sanitize new pro features
5819 5799 if (isset($input['close_button_color'])) {
5820 5800 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5821 5801 }
5822 5802
@@ -6028,9 +6008,9 @@
6028 6008
6029 6009
6030 6010 private static function mxchat_extract_main_content($html) {
6031 6011 if (empty($html)) {
6032 - //error_log('mxchat_extract_main_content: Empty HTML content received');
6012 + error_log('mxchat_extract_main_content: Empty HTML content received');
6033 6013 return '';
6034 6014 }
6035 6015
6036 6016 try {
@@ -6035,14 +6015,14 @@
6035 6015
6036 6016 try {
6037 6017 $dom = new DOMDocument;
6038 6018 libxml_use_internal_errors(true); // Suppress HTML parsing errors
6039 -
6019 +
6040 6020 // Simple load of HTML, with @ to suppress warnings
6041 6021 @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
6042 -
6022 +
6043 6023 $xpath = new DOMXPath($dom);
6044 -
6024 +
6045 6025 // Simplified selectors focusing on common content areas
6046 6026 $selectors = [
6047 6027 '//article',
6048 6028 '//*[@id="content"]',
@@ -6048,9 +6028,9 @@
6048 6028 '//*[@id="content"]',
6049 6029 '//*[@class="entry-content"]',
6050 6030 '//main'
6051 6031 ];
6052 -
6032 +
6053 6033 foreach ($selectors as $selector) {
6054 6034 $nodes = $xpath->query($selector);
6055 6035 if ($nodes && $nodes->length > 0) {
6056 6036 $content = '';
@@ -6061,20 +6041,20 @@
6061 6041 return $content;
6062 6042 }
6063 6043 }
6064 6044 }
6065 -
6045 +
6066 6046 // Fallback: Return the entire body content if no specific selector matches
6067 6047 $body = $dom->getElementsByTagName('body');
6068 6048 if ($body->length > 0) {
6069 6049 return $dom->saveHTML($body->item(0));
6070 6050 }
6071 -
6051 +
6072 6052 // Last resort: return the original HTML
6073 6053 return $html;
6074 -
6054 +
6075 6055 } catch (Exception $e) {
6076 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
6056 + error_log('mxchat_extract_main_content error: ' . $e->getMessage());
6077 6057 return $html; // Return original HTML if parsing fails
6078 6058 } finally {
6079 6059 libxml_clear_errors();
6080 6060 }