PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.8
MxChat – AI Chatbot & Content Generation for WordPress v2.5.8
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 +2818 -3053 3.2.12.5.8 View file →
@@ -8,24 +8,8 @@
8 8 private $chat_count;
9 9 private $is_activated;
10 10 private $knowledge_manager;
11 11
12 - /**
13 - * Get whether the license is activated
14 - * @return bool
15 - */
16 - public function is_activated() {
17 - return $this->is_activated;
18 - }
19 -
20 - /**
21 - * Get plugin options
22 - * @return array
23 - */
24 - public function get_options() {
25 - return $this->options;
26 - }
27 -
28 12 public function __construct($knowledge_manager = null) {
29 13 $this->options = get_option('mxchat_options');
30 14 $this->chat_count = get_option('mxchat_chat_count', 0);
31 15 $this->is_activated = $this->is_license_active();
@@ -44,9 +28,8 @@
44 28 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
45 29 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
46 30 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
47 31 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
48 - add_action('wp_ajax_mxchat_fetch_conversation', array($this, 'mxchat_fetch_conversation'));
49 32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
50 33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
51 34 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
52 35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
@@ -56,47 +39,27 @@
56 39 add_action('admin_post_mxchat_edit_intent', array($this, 'mxchat_handle_edit_intent'));
57 40 add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
58 41 add_action('admin_init', array($this, 'mxchat_transcripts_page_init'));
59 42 add_action('wp_ajax_dismiss_live_agent_notice', array($this, 'dismiss_live_agent_notice'));
60 - add_action('wp_ajax_dismiss_theme_migration_notice', array($this, 'dismiss_theme_migration_notice'));
61 43 add_action('mxchat_cleanup_old_transcripts', array($this, 'cleanup_old_transcripts'));
62 44
63 45 add_action('admin_init', array($this, 'register_pinecone_settings'));
64 - add_action('admin_init', array($this, 'register_openai_vectorstore_settings'));
65 46
66 47 add_action('admin_notices', array($this, 'display_admin_notices'));
67 48
68 49 add_action('wp_ajax_mxchat_test_streaming_actual', [$this, 'mxchat_handle_test_streaming_actual']);
69 50 add_action('wp_ajax_mxchat_test_streaming', [$this, 'mxchat_handle_test_streaming']); // Keep existing as fallback
70 - add_action('wp_ajax_mxchat_test_vectorstore_connection', array($this, 'mxchat_test_vectorstore_connection'));
71 51
72 52 add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
73 53 add_action('wp_ajax_mxchat_fetch_openrouter_models', array($this, 'fetch_openrouter_models'));
74 - add_action('wp_ajax_mxchat_get_rag_context', array($this, 'mxchat_get_rag_context'));
75 -
76 - // Actions page AJAX handlers
77 - add_action('wp_ajax_mxchat_fetch_actions_list', array($this, 'mxchat_fetch_actions_list'));
78 - add_action('wp_ajax_mxchat_toggle_action_status', array($this, 'mxchat_toggle_action_status'));
79 - add_action('wp_ajax_mxchat_bulk_delete_actions', array($this, 'mxchat_bulk_delete_actions'));
80 - add_action('wp_ajax_mxchat_add_intent_ajax', array($this, 'mxchat_add_intent_ajax'));
81 - add_action('wp_ajax_mxchat_edit_intent_ajax', array($this, 'mxchat_edit_intent_ajax'));
82 - add_action('wp_ajax_mxchat_delete_intent_ajax', array($this, 'mxchat_delete_intent_ajax'));
83 - add_action('wp_ajax_mxchat_add_phrase', array($this, 'mxchat_add_phrase_ajax'));
84 - add_action('wp_ajax_mxchat_delete_phrase', array($this, 'mxchat_delete_phrase_ajax'));
85 - add_action('wp_ajax_mxchat_get_phrases', array($this, 'mxchat_get_phrases_ajax'));
86 - add_action('wp_ajax_mxchat_delete_legacy_phrases', array($this, 'mxchat_delete_legacy_phrases_ajax'));
87 -
88 - // Slack test connection
89 - add_action('wp_ajax_mxchat_test_slack_connection', array($this, 'mxchat_test_slack_connection'));
90 -
91 - // Translation handlers
92 - add_action('wp_ajax_mxchat_translate_messages', array($this, 'mxchat_translate_messages'));
93 - add_action('wp_ajax_mxchat_get_transcript_translation', array($this, 'mxchat_get_transcript_translation'));
94 54 }
95 55
96 56 private function is_license_active() {
57 + // Get the raw value without translation
97 58 $license_status = get_option('mxchat_license_status', 'inactive');
98 - return ($license_status === 'active');
59 +
60 + // Check against multiple possible values, bypassing translation issues
61 + return ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
99 62 }
100 63
101 64 private function initialize_default_options() {
102 65 $default_options = array(
@@ -105,10 +68,9 @@
105 68 'claude_api_key' => '',
106 69 'deepseek_api_key' => '',
107 70 'voyage_api_key' => '',
108 71 'gemini_api_key' => '',
109 - 'enable_streaming_toggle' => 'off',
110 - 'enable_web_search' => 'off',
72 + 'enable_streaming_toggle' => 'on',
111 73 'embedding_model' => 'text-embedding-ada-002',
112 74 'system_prompt_instructions' => 'You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
113 75
114 76 # Response Style - CRITICALLY IMPORTANT
@@ -127,9 +89,9 @@
127 89 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
128 90 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
129 91 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
130 92 - Better to admit insufficient information than provide inaccurate answers',
131 - 'model' => esc_html__('gpt-5.1-chat-latest', 'mxchat'),
93 + 'model' => esc_html__('gpt-4o', 'mxchat'),
132 94 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
133 95 'role_rate_limits' => array(),
134 96 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
135 97 'enable_email_block' => '',
@@ -141,12 +103,9 @@
141 103 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
142 104 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
143 105 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
144 106 'append_to_body' => esc_html__('off', 'mxchat'),
145 - 'post_type_visibility_mode' => 'all', // 'all', 'include', 'exclude'
146 - 'post_type_visibility_list' => array(), // Array of post type slugs
147 107 'contextual_awareness_toggle' => 'off',
148 - 'citation_links_toggle' => 'on',
149 108 'close_button_color' => esc_html__('#fff', 'mxchat'),
150 109 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
151 110 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
152 111 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
@@ -175,33 +134,18 @@
175 134 'pdf_max_pages' => 69,
176 135 'show_pdf_upload_button' => 'on',
177 136 'show_word_upload_button' => 'on',
178 137
179 - // Live Agent Integration (Slack)
138 + // Live Agent Integration
180 139 'live_agent_webhook_url' => '',
181 140 'live_agent_secret_key' => '',
182 141 'live_agent_bot_token' => '',
183 142 'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'),
184 143 'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'),
185 -
186 - // Telegram Integration
187 - 'telegram_status' => 'off',
188 - 'telegram_bot_token' => '',
189 - 'telegram_group_id' => '',
190 - 'telegram_webhook_secret' => '',
191 - 'telegram_notification_message' => __("I've notified a support agent. Please allow a moment for them to respond. If you'd like to continue with AI, just type \"Switch to AI\" at any time.", 'mxchat'),
192 - 'telegram_away_message' => __("I just checked, and it looks like our support team isn't personally available at the moment. If you'd like, you can leave your email address, and they'll get back to you as soon as possible. In the meantime, you can keep chatting with me — just let me know how I can help!", 'mxchat'),
193 -
194 144 'chat_toolbar_toggle' => esc_html__('off', 'mxchat'),
195 145 'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'),
196 146 'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'),
197 147 'toolbar_icon_color' => esc_html__('#212121', 'mxchat'),
198 -
199 - // Optimization settings
200 - 'script_loading_strategy' => 'default',
201 -
202 - // Debug settings
203 - 'debug_mode' => 'off',
204 148 );
205 149
206 150
207 151 // Merge existing options with defaults
@@ -236,18 +180,8 @@
236 180 'dashicons-testimonial',
237 181 6
238 182 );
239 183
240 - // Rename the first submenu item from "MxChat" to "Settings"
241 - add_submenu_page(
242 - 'mxchat-max',
243 - esc_html__('MxChat Settings', 'mxchat'),
244 - esc_html__('Settings', 'mxchat'),
245 - 'manage_options',
246 - 'mxchat-max',
247 - array($this, 'mxchat_create_admin_page')
248 - );
249 -
250 184 // Submenu page for Knowledge
251 185 add_submenu_page(
252 186 'mxchat-max',
253 187 esc_html__('Prompts', 'mxchat'),
@@ -274,23 +208,22 @@
274 208 'mxchat-actions',
275 209 array($this, 'mxchat_actions_page_html')
276 210 );
277 211
278 - // Content Generator page
279 - add_submenu_page(
280 - 'mxchat-max',
281 - esc_html__('Content', 'mxchat'),
282 - esc_html__('Content', 'mxchat'),
283 - 'manage_options',
284 - 'mxchat-content',
285 - array($this, 'mxchat_create_content_page')
286 - );
212 + add_submenu_page(
213 + 'mxchat-max',
214 + esc_html__('Add Ons', 'mxchat'),
215 + esc_html__('Add Ons', 'mxchat'),
216 + 'manage_options',
217 + 'mxchat-addons',
218 + array($this, 'mxchat_create_addons_page')
219 + );
287 220
288 - // Consolidated Pro & Extensions page (replaces separate Add Ons and Pro Upgrade pages)
221 + // Submenu page for Activation Key
289 222 add_submenu_page(
290 223 'mxchat-max',
291 - esc_html__('Pro & Extensions', 'mxchat'),
292 - esc_html__('Pro & Extensions', 'mxchat'),
224 + esc_html__('Pro Upgrade', 'mxchat'),
225 + esc_html__('Pro Upgrade', 'mxchat'),
293 226 'manage_options',
294 227 'mxchat-activation',
295 228 array($this, 'mxchat_create_activation_page')
296 229 );
@@ -302,16 +235,8 @@
302 235 $addons_page->render_page();
303 236 }
304 237
305 238 /**
306 - * Render the Content Generator admin page
307 - */
308 -public function mxchat_create_content_page() {
309 - require_once plugin_dir_path(__FILE__) . 'admin-content-page.php';
310 - mxchat_render_content_page($this);
311 -}
312 -
313 -/**
314 239 * Test actual streaming functionality in the WordPress environment
315 240 */
316 241 public function mxchat_handle_test_streaming_actual() {
317 242 check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
@@ -329,9 +254,9 @@
329 254 }
330 255
331 256 // Get user's selected model and API key
332 257 $options = get_option('mxchat_options', []);
333 - $selected_model = $options['model'] ?? 'gpt-5.1-chat-latest';
258 + $selected_model = $options['model'] ?? 'gpt-4o';
334 259
335 260 // Get the provider from the model
336 261 $model_parts = explode('-', $selected_model);
337 262 $provider = strtolower($model_parts[0]);
@@ -600,236 +525,8 @@
600 525
601 526 return $sanitized;
602 527 }
603 528
604 -public function register_openai_vectorstore_settings() {
605 - register_setting(
606 - 'mxchat_openai_vectorstore_options',
607 - 'mxchat_openai_vectorstore_options',
608 - array(
609 - 'type' => 'array',
610 - 'sanitize_callback' => array($this, 'sanitize_openai_vectorstore_settings'),
611 - 'default' => array(
612 - 'mxchat_use_openai_vectorstore' => '0',
613 - 'mxchat_vectorstore_ids' => '',
614 - 'mxchat_vectorstore_max_results' => 5
615 - )
616 - )
617 - );
618 -}
619 -
620 -public function sanitize_openai_vectorstore_settings($input) {
621 - $sanitized = array();
622 -
623 - $sanitized['mxchat_use_openai_vectorstore'] = isset($input['mxchat_use_openai_vectorstore']) ? '1' : '0';
624 - $sanitized['mxchat_vectorstore_ids'] = sanitize_text_field($input['mxchat_vectorstore_ids'] ?? '');
625 - $sanitized['mxchat_vectorstore_max_results'] = absint($input['mxchat_vectorstore_max_results'] ?? 5);
626 -
627 - // Ensure max results is within reasonable range
628 - if ($sanitized['mxchat_vectorstore_max_results'] < 1) {
629 - $sanitized['mxchat_vectorstore_max_results'] = 1;
630 - }
631 - if ($sanitized['mxchat_vectorstore_max_results'] > 20) {
632 - $sanitized['mxchat_vectorstore_max_results'] = 20;
633 - }
634 -
635 - return $sanitized;
636 -}
637 -
638 -/**
639 - * AJAX handler to test OpenAI Vector Store connection
640 - */
641 -public function mxchat_test_vectorstore_connection() {
642 - check_ajax_referer('mxchat_admin_nonce', 'nonce');
643 -
644 - if (!current_user_can('manage_options')) {
645 - wp_send_json_error(array('message' => __('Permission denied.', 'mxchat')));
646 - return;
647 - }
648 -
649 - $vectorstore_options = get_option('mxchat_openai_vectorstore_options', array());
650 - $vectorstore_ids = $vectorstore_options['mxchat_vectorstore_ids'] ?? '';
651 - $mxchat_options = get_option('mxchat_options', array());
652 - $api_key = $mxchat_options['api_key'] ?? '';
653 -
654 - if (empty($api_key)) {
655 - wp_send_json_error(array('message' => __('OpenAI API key is not configured.', 'mxchat')));
656 - return;
657 - }
658 -
659 - if (empty($vectorstore_ids)) {
660 - wp_send_json_error(array('message' => __('No Vector Store ID configured.', 'mxchat')));
661 - return;
662 - }
663 -
664 - // Get the first Vector Store ID for testing
665 - $ids_array = array_map('trim', explode(',', $vectorstore_ids));
666 - $test_id = $ids_array[0];
667 -
668 - // Test by retrieving the Vector Store info
669 - $response = wp_remote_get(
670 - 'https://api.openai.com/v1/vector_stores/' . $test_id,
671 - array(
672 - 'headers' => array(
673 - 'Authorization' => 'Bearer ' . $api_key,
674 - 'Content-Type' => 'application/json',
675 - 'OpenAI-Beta' => 'assistants=v2'
676 - ),
677 - 'timeout' => 30
678 - )
679 - );
680 -
681 - if (is_wp_error($response)) {
682 - wp_send_json_error(array('message' => __('Connection failed: ', 'mxchat') . $response->get_error_message()));
683 - return;
684 - }
685 -
686 - $status_code = wp_remote_retrieve_response_code($response);
687 - $body = json_decode(wp_remote_retrieve_body($response), true);
688 -
689 - if ($status_code === 200 && isset($body['id'])) {
690 - $file_count = $body['file_counts']['completed'] ?? 0;
691 - $name = $body['name'] ?? $test_id;
692 - wp_send_json_success(array(
693 - 'message' => sprintf(
694 - __('Connected successfully! Vector Store: %s (%d files)', 'mxchat'),
695 - esc_html($name),
696 - $file_count
697 - )
698 - ));
699 - } elseif ($status_code === 404) {
700 - wp_send_json_error(array('message' => __('Vector Store not found. Please check the ID.', 'mxchat')));
701 - } elseif ($status_code === 401) {
702 - wp_send_json_error(array('message' => __('Invalid API key.', 'mxchat')));
703 - } else {
704 - $error_message = $body['error']['message'] ?? __('Unknown error occurred.', 'mxchat');
705 - wp_send_json_error(array('message' => $error_message));
706 - }
707 -}
708 -
709 -/**
710 - * AJAX handler to test Slack connection and validate scopes
711 - */
712 -public function mxchat_test_slack_connection() {
713 - check_ajax_referer('mxchat_admin_nonce', 'nonce');
714 -
715 - if (!current_user_can('manage_options')) {
716 - wp_send_json_error(array('message' => __('Permission denied.', 'mxchat')));
717 - return;
718 - }
719 -
720 - $bot_token = $this->options['live_agent_bot_token'] ?? '';
721 -
722 - if (empty($bot_token)) {
723 - wp_send_json_error(array('message' => __('Slack Bot Token is not configured. Please enter your token and save settings first.', 'mxchat')));
724 - return;
725 - }
726 -
727 - // Test 1: Validate bot token with auth.test
728 - $auth_response = wp_remote_post('https://slack.com/api/auth.test', array(
729 - 'headers' => array(
730 - 'Authorization' => 'Bearer ' . $bot_token,
731 - 'Content-Type' => 'application/json'
732 - ),
733 - 'timeout' => 15
734 - ));
735 -
736 - if (is_wp_error($auth_response)) {
737 - wp_send_json_error(array('message' => __('Connection failed: ', 'mxchat') . $auth_response->get_error_message()));
738 - return;
739 - }
740 -
741 - $auth_body = json_decode(wp_remote_retrieve_body($auth_response), true);
742 -
743 - if (!isset($auth_body['ok']) || !$auth_body['ok']) {
744 - $error = $auth_body['error'] ?? 'unknown_error';
745 - $error_messages = array(
746 - 'invalid_auth' => __('Invalid bot token. Please check your token starts with xoxb-', 'mxchat'),
747 - 'not_authed' => __('No authentication token provided.', 'mxchat'),
748 - 'account_inactive' => __('The Slack workspace has been deactivated.', 'mxchat'),
749 - 'token_revoked' => __('The bot token has been revoked. Please generate a new one.', 'mxchat'),
750 - );
751 - $message = $error_messages[$error] ?? sprintf(__('Authentication failed: %s', 'mxchat'), $error);
752 - wp_send_json_error(array('message' => $message));
753 - return;
754 - }
755 -
756 - $team_name = $auth_body['team'] ?? 'Unknown Workspace';
757 - $bot_name = $auth_body['user'] ?? 'Unknown Bot';
758 -
759 - // Test 2: Check if we can list channels (tests channels:read scope)
760 - $channels_response = wp_remote_post('https://slack.com/api/conversations.list', array(
761 - 'headers' => array(
762 - 'Authorization' => 'Bearer ' . $bot_token,
763 - 'Content-Type' => 'application/json'
764 - ),
765 - 'body' => json_encode(array('limit' => 1)),
766 - 'timeout' => 15
767 - ));
768 -
769 - $channels_body = json_decode(wp_remote_retrieve_body($channels_response), true);
770 - $can_read_channels = isset($channels_body['ok']) && $channels_body['ok'];
771 -
772 - // Test 3: Check if we can create channels (tests channels:manage scope)
773 - // We'll just check the error message without actually creating
774 - $create_response = wp_remote_post('https://slack.com/api/conversations.create', array(
775 - 'headers' => array(
776 - 'Authorization' => 'Bearer ' . $bot_token,
777 - 'Content-Type' => 'application/json'
778 - ),
779 - 'body' => json_encode(array('name' => 'mxchat-test-' . time(), 'is_private' => false)),
780 - 'timeout' => 15
781 - ));
782 -
783 - $create_body = json_decode(wp_remote_retrieve_body($create_response), true);
784 -
785 - // If channel was created, delete it immediately
786 - if (isset($create_body['ok']) && $create_body['ok'] && isset($create_body['channel']['id'])) {
787 - wp_remote_post('https://slack.com/api/conversations.archive', array(
788 - 'headers' => array(
789 - 'Authorization' => 'Bearer ' . $bot_token,
790 - 'Content-Type' => 'application/json'
791 - ),
792 - 'body' => json_encode(array('channel' => $create_body['channel']['id'])),
793 - 'timeout' => 15
794 - ));
795 - $can_create_channels = true;
796 - } else {
797 - $create_error = $create_body['error'] ?? '';
798 - // name_taken means we have permission but channel exists
799 - $can_create_channels = ($create_error === 'name_taken' || (isset($create_body['ok']) && $create_body['ok']));
800 -
801 - // Check for missing scope errors
802 - if ($create_error === 'missing_scope') {
803 - $can_create_channels = false;
804 - }
805 - }
806 -
807 - // Build result message
808 - $results = array();
809 - $results[] = sprintf(__('Workspace: %s', 'mxchat'), esc_html($team_name));
810 - $results[] = sprintf(__('Bot: %s', 'mxchat'), esc_html($bot_name));
811 - $results[] = '';
812 - $results[] = ($can_read_channels ? '✓' : '✗') . ' ' . __('channels:read - List channels', 'mxchat');
813 - $results[] = ($can_create_channels ? '✓' : '✗') . ' ' . __('channels:manage - Create channels', 'mxchat');
814 -
815 - $missing_scopes = array();
816 - if (!$can_read_channels) $missing_scopes[] = 'channels:read';
817 - if (!$can_create_channels) $missing_scopes[] = 'channels:manage';
818 -
819 - if (!empty($missing_scopes)) {
820 - wp_send_json_error(array(
821 - 'message' => implode("\n", $results),
822 - 'missing_scopes' => $missing_scopes,
823 - 'partial' => true
824 - ));
825 - } else {
826 - wp_send_json_success(array(
827 - 'message' => implode("\n", $results)
828 - ));
829 - }
830 -}
831 -
832 529 public function mxchat_display_admin_notice() {
833 530 // Success notice
834 531 if ($message = get_transient('mxchat_admin_notice_success')) {
835 532 ?>
@@ -876,13 +573,282 @@
876 573 }
877 574 }
878 575 public function mxchat_create_admin_page() {
879 576 $this->add_live_agent_nonce();
880 - $this->add_theme_migration_nonce();
881 577
882 - // Include and render the new sidebar-based settings page
883 - require_once plugin_dir_path(__FILE__) . 'admin-settings-page.php';
884 - mxchat_render_settings_page($this);
578 + ?>
579 + <div class="wrap mxchat-wrapper">
580 + <!-- Hero Section -->
581 + <div class="mxchat-hero">
582 + <h1 class="mxchat-main-title">
583 + <span class="mxchat-gradient-text">MxChat</span> Settings
584 + </h1>
585 + <p class="mxchat-hero-subtitle">
586 + <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
587 + </p>
588 + </div>
589 +
590 + <div class="mxchat-content">
591 +
592 + <?php if (!$this->is_activated): ?>
593 + <div class="mxchat-pro-card">
594 + <div class="mxchat-pro-notification">
595 + <div class="mxchat-pro-content">
596 + <h3>🚀 Limited Lifetime Offer: Save 15% on MxChat Pro, Agency, or Agency Plus!</h3>
597 + <p>Unlock <strong>unlimited access</strong> to our growing collection of powerful add-ons including Admin AI Assistant (ChatGPT-like experience), Forms Builder, AI Theme Generator, WooCommerce, multi-bot, and more – all included with your <strong>lifetime license!</strong></p> </div>
598 + <div class="mxchat-pro-cta">
599 + <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade Today', 'mxchat'); ?></a>
600 + <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
601 + </div>
602 + </div>
603 + </div>
604 + <?php endif; ?>
605 +
606 + <?php
607 + $this->show_live_agent_disabled_banner();
608 + ?>
609 +
610 + <!-- Tabs Navigation -->
611 + <div class="mxchat-tabs">
612 + <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
613 + <button class="mxchat-tab-button" data-tab="api-keys"><?php echo esc_html__('API Keys', 'mxchat'); ?></button>
614 + <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
615 + <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
616 + </div>
617 +
618 + <!-- Tab Contents -->
619 + <div id="chatbot" class="mxchat-tab-content active">
620 + <div class="mxchat-card">
621 + <div class="mxchat-autosave-section">
622 + <?php do_settings_sections('mxchat-chatbot'); ?>
623 + </div>
624 + </div>
625 + </div>
626 +
627 + <div id="api-keys" class="mxchat-tab-content">
628 + <div class="mxchat-card">
629 + <div class="mxchat-autosave-section">
630 + <table class="form-table">
631 + <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
632 + </table>
633 + </div>
634 + </div>
635 + </div>
636 +
637 + <div id="embed" class="mxchat-tab-content">
638 +
639 + <div class="mxchat-card">
640 + <h2><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h2>
641 + <div class="mxchat-autosave-section">
642 + <table class="form-table">
643 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
644 + </table>
645 + </div>
646 + </div>
647 +
648 +
649 + <div class="mxchat-card">
650 + <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
651 + <div class="mxchat-autosave-section">
652 + <table class="form-table">
653 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
654 + </table>
655 + </div>
656 + </div>
657 +
658 + <div class="mxchat-card">
659 + <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
660 + <div class="mxchat-autosave-section">
661 + <table class="form-table">
662 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
663 + </table>
664 + </div>
665 + </div>
666 +
667 + <div class="mxchat-card">
668 + <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
669 + <div class="mxchat-autosave-section">
670 +<p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('for setup instructions and to see what\'s changed in the latest update.', 'mxchat'); ?></p> <table class="form-table">
671 + <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
672 + </table>
673 + </div>
674 + </div>
675 + </div>
676 +
677 + <div id="general" class="mxchat-tab-content">
678 + <div class="mxchat-card">
679 + <?php do_settings_sections('mxchat-general'); ?>
680 + <div class="video-tutorials-section">
681 +
682 +
683 + <div class="support-notification">
684 + <div class="support-notification-icon">
685 + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
686 + <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
687 + <path d="M12 9v4"/>
688 + <path d="M12 17h.01"/>
689 + </svg>
690 + </div>
691 + <div class="support-notification-content">
692 + <h3 class="support-notification-title"><?php echo esc_html__('Need Help? We\'re Here for You!', 'mxchat'); ?></h3>
693 + <p class="support-notification-message">
694 + <?php echo esc_html__('Our goal is to provide the best experience and AI chatbot plugin available. If you\'re having trouble or believe something is not working as expected, please don\'t hesitate to submit a support ticket.', 'mxchat'); ?>
695 + </p>
696 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer" class="support-notification-button">
697 + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
698 + <path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/>
699 + <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/>
700 + </svg>
701 + <?php echo esc_html__('Submit Support Ticket', 'mxchat'); ?>
702 + </a>
703 + </div>
704 +</div>
705 +
706 + <div class="tutorial-grid">
707 +
708 + <div class="tutorial-item">
709 + <h3><?php echo esc_html__('AI Theme Generator Tutorial', 'mxchat'); ?></h3>
710 + <div class="video-description">
711 + <p><?php echo esc_html__('Learn how to instantly restyle your chatbot using plain English prompts. The AI Theme Generator lets you generate and apply beautiful designs with real-time previews—no CSS skills needed.', 'mxchat'); ?></p>
712 + <a href="https://www.youtube.com/watch?v=rSQDW2qbtRU&t" target="_blank" rel="noopener" class="video-link">
713 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
714 + <?php echo esc_html__('Watch AI Theme Generator Tutorial', 'mxchat'); ?>
715 + </a>
716 + </div>
717 +</div>
718 +
719 +
720 + <div class="tutorial-item">
721 + <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
722 + <div class="video-description">
723 + <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
724 + <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
725 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
726 + <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
727 + </a>
728 + </div>
729 + </div>
730 +
731 +<div class="tutorial-item">
732 + <h3><?php echo esc_html__('Admin Assistant Add-on', 'mxchat'); ?></h3>
733 + <div class="video-description">
734 + <p><?php echo esc_html__('Discover how to use the MxChat Admin Assistant to bring a ChatGPT-like experience directly inside your WordPress dashboard. Learn to access multiple AI models, save conversations, generate images, and use web search - all without leaving your admin panel.', 'mxchat'); ?></p>
735 + <a href="https://youtu.be/AdEA1k-UCFM" target="_blank" rel="noopener" class="video-link">
736 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2-3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
737 + <?php echo esc_html__('Watch Admin Assistant Tutorial', 'mxchat'); ?>
738 + </a>
739 + </div>
740 +</div>
741 +
742 + <div class="tutorial-item">
743 + <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
744 + <div class="video-description">
745 + <p><?php echo esc_html__('Discover how to use the Intent Tester to fine-tune your chatbot\'s responses and ensure it accurately understands user queries.', 'mxchat'); ?></p>
746 + <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
747 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
748 + <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
749 + </a>
750 + </div>
751 + </div>
752 +
753 + <div class="tutorial-item">
754 + <h3><?php echo esc_html__('Theme Customizer Add-on', 'mxchat'); ?></h3>
755 + <div class="video-description">
756 + <p><?php echo esc_html__('Learn how to customize your chatbot appearance with the Theme Customizer add-on. Easily modify colors, fonts, and styles with real-time previews to match your brand perfectly.', 'mxchat'); ?></p>
757 + <a href="https://youtu.be/MfbB9mZi6ag" target="_blank" rel="noopener" class="video-link">
758 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
759 + <?php echo esc_html__('Watch Theme Customizer Tutorial', 'mxchat'); ?>
760 + </a>
761 + </div>
762 + </div>
763 +
764 + <div class="tutorial-item">
765 + <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
766 + <div class="video-description">
767 + <p><?php echo esc_html__('See how to integrate MxChat with your WooCommerce store to provide product recommendations and shopping assistance to your customers.', 'mxchat'); ?></p>
768 + <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
769 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
770 + <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
771 + </a>
772 + </div>
773 + </div>
774 +
775 + <div class="tutorial-item">
776 + <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
777 + <div class="video-description">
778 + <p><?php echo esc_html__('Learn how to set up your knowledge base using PDFs, sitemaps, and manual entries to enhance your chatbot\'s responses with site-specific information.', 'mxchat'); ?></p>
779 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
780 + <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
781 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
782 + <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
783 + </a>
784 + </div>
785 + </div>
786 +
787 + <div class="tutorial-item">
788 + <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
789 + <div class="video-description">
790 + <p><?php echo esc_html__('See how to use the MxChat toolbar to chat with PDF and Word documents for enhanced document analysis and information retrieval.', 'mxchat'); ?></p>
791 + <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
792 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
793 + <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
794 + </a>
795 + </div>
796 + </div>
797 +
798 + <div class="tutorial-item">
799 + <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
800 + <div class="video-description">
801 + <p><?php echo esc_html__('Learn how to integrate Perplexity with your chatbot for real-time web search capabilities. This tutorial covers intent recognition, the toolbar toggle button, and how to enable your chatbot to search the web and provide up-to-date information to your visitors.', 'mxchat'); ?></p>
802 + <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
803 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
804 + <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
805 + </a>
806 + </div>
807 + </div>
808 +
809 + <div class="tutorial-item">
810 + <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
811 + <div class="video-description">
812 + <p><?php echo esc_html__('Learn how to leverage Brave Search intent capabilities to improve your chatbot\'s understanding of user queries and provide more accurate responses.', 'mxchat'); ?></p>
813 + <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
814 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
815 + <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
816 + </a>
817 + </div>
818 + </div>
819 +
820 + <div class="tutorial-item">
821 + <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
822 + <div class="video-description">
823 + <p><?php echo esc_html__('Discover how to set up email capture with MxChat using Loops to grow your mailing list while providing value through your chatbot.', 'mxchat'); ?></p>
824 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
825 + <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
826 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
827 + <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
828 + </a>
829 + </div>
830 + </div>
831 +
832 + <div class="tutorial-item">
833 + <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
834 + <div class="video-description">
835 + <p><?php echo esc_html__('Learn how to use the MxChat AI Agent Testing Service to evaluate and improve your chatbot\'s performance and accuracy.', 'mxchat'); ?></p>
836 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
837 + <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
838 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
839 + <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
840 + </a>
841 + </div>
842 + </div>
843 + </div>
844 +
845 + </div>
846 + </div>
847 + </div>
848 + </div>
849 + </div>
850 + <?php
885 851 }
886 852
887 853 public function dismiss_live_agent_notice() {
888 854 // Add debugging
@@ -910,75 +876,16 @@
910 876 ));
911 877 }
912 878 }
913 879
914 -/**
915 - * Show theme migration notice for Pro users with AI-generated themes
916 - * Only shown once - dismissible and stored in options
917 - */
918 -public function show_theme_migration_banner() {
919 - // Only show if Pro is activated
920 - if (!$this->is_activated) {
921 - return;
922 - }
923 880
924 - // Check if notice should be shown
925 - $show_notice = get_option('mxchat_show_theme_migration_notice', false);
926 -
927 - if ($show_notice) {
928 - ?>
929 - <div class="mxchat-theme-migration-notice" id="mxchat-theme-migration-notice">
930 - <div class="mxchat-pro-notification">
931 - <button type="button" class="mxchat-dismiss-btn" onclick="dismissThemeMigrationNotice()" aria-label="Dismiss notification">
932 - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
933 - <line x1="18" y1="6" x2="6" y2="18"></line>
934 - <line x1="6" y1="6" x2="18" y2="18"></line>
935 - </svg>
936 - </button>
937 - <div class="mxchat-theme-migration-content">
938 - <h3>🎨 AI Theme Migration Required</h3>
939 - <p>If you're using an AI-generated chatbot theme, you'll need to migrate it to match the new CSS structure. Go to <strong>Theme Settings</strong>, select your theme from the sidebar, and click the <strong>Migrate</strong> button.</p>
940 - </div>
941 - </div>
942 - </div>
943 - <?php
944 - }
945 -}
946 -
947 -/**
948 - * Dismiss theme migration notice via AJAX
949 - */
950 -public function dismiss_theme_migration_notice() {
951 - // Verify nonce
952 - if (!wp_verify_nonce($_POST['nonce'], 'dismiss_theme_migration_notice')) {
953 - wp_die('Security check failed');
954 - }
955 -
956 - // Remove the notice flag
957 - delete_option('mxchat_show_theme_migration_notice');
958 -
959 - wp_send_json_success();
960 -}
961 -
962 -/**
963 - * Add nonce for theme migration notice dismiss
964 - */
965 -public function add_theme_migration_nonce() {
966 - if (get_option('mxchat_show_theme_migration_notice', false) && $this->is_activated) {
967 - wp_localize_script('mxchat-admin-js', 'mxchatThemeMigration', array(
968 - 'nonce' => wp_create_nonce('dismiss_theme_migration_notice'),
969 - 'ajaxurl' => admin_url('admin-ajax.php')
970 - ));
971 - }
972 -}
973 -
974 881 public function mxchat_create_transcripts_page() {
975 882 global $wpdb;
976 883 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
977 884
978 885 // Get basic stats
979 - $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name") ?: 0;
980 - $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name") ?: 0;
886 + $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
887 + $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
981 888
982 889 // Count unique users with detailed breakdown
983 890 $total_users = $wpdb->get_var("
984 891 SELECT COUNT(DISTINCT
@@ -1116,89 +1023,298 @@
1116 1023 $chart_chats[] = 0;
1117 1024 $chart_messages[] = 0;
1118 1025 }
1119 1026 }
1027 + ?>
1028 + <div class="wrap mxchat-transcripts-wrapper">
1029 + <!-- Hero Section -->
1030 + <div class="mxchat-transcripts-hero">
1031 + <h1 class="mxchat-main-title">
1032 + Chat <span class="mxchat-gradient-text">Transcripts</span>
1033 + </h1>
1034 + <p class="mxchat-hero-subtitle">
1035 + <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
1036 + </p>
1037 + </div>
1038 + <div class="mxchat-content">
1039 + <!-- Enhanced Metrics Dashboard -->
1040 + <div class="mxchat-metrics-dashboard">
1041 + <!-- Tab Navigation -->
1042 + <div class="mxchat-metrics-tabs">
1043 + <button class="mxchat-metrics-tab active" data-tab="overview">
1044 + <span class="tab-icon">📊</span>
1045 + <span class="tab-label">Overview</span>
1046 + </button>
1047 + <button class="mxchat-metrics-tab" data-tab="activity">
1048 + <span class="tab-icon">📈</span>
1049 + <span class="tab-label">Activity</span>
1050 + </button>
1051 + <button class="mxchat-metrics-tab" data-tab="insights">
1052 + <span class="tab-icon">💡</span>
1053 + <span class="tab-label">Insights</span>
1054 + </button>
1055 + </div>
1056 +
1057 + <!-- Tab Content -->
1058 + <div class="mxchat-metrics-content">
1059 + <!-- Overview Tab -->
1060 + <div class="mxchat-metrics-panel active" data-panel="overview">
1061 + <div class="mxchat-stats-grid">
1062 + <div class="mxchat-stat-card mxchat-stat-primary">
1063 + <div class="stat-icon">💬</div>
1064 + <div class="stat-content">
1065 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
1066 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
1067 + </div>
1068 + </div>
1069 + <div class="mxchat-stat-card mxchat-stat-primary">
1070 + <div class="stat-icon">📝</div>
1071 + <div class="stat-content">
1072 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
1073 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
1074 + </div>
1075 + </div>
1076 + <div class="mxchat-stat-card mxchat-stat-primary">
1077 + <div class="stat-icon">👥</div>
1078 + <div class="stat-content">
1079 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
1080 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
1081 + <span class="stat-sublabel">
1082 + <?php
1083 + echo sprintf(
1084 + esc_html__('%d registered • %d guests • %d tests', 'mxchat'),
1085 + $registered_users,
1086 + $guest_users,
1087 + $agent_tests
1088 + );
1089 + ?>
1090 + </span>
1091 + </div>
1092 + </div>
1093 + <div class="mxchat-stat-card mxchat-stat-primary">
1094 + <div class="stat-icon">💭</div>
1095 + <div class="stat-content">
1096 + <span class="stat-value"><?php echo esc_html($avg_messages); ?></span>
1097 + <span class="stat-label"><?php esc_html_e('Avg Messages/Chat', 'mxchat'); ?></span>
1098 + </div>
1099 + </div>
1100 + </div>
1101 + </div>
1102 +
1103 + <!-- Activity Tab -->
1104 + <div class="mxchat-metrics-panel" data-panel="activity">
1105 + <div class="mxchat-activity-layout">
1106 + <div class="mxchat-activity-stats">
1107 + <div class="mxchat-stat-card mxchat-stat-accent">
1108 + <div class="stat-icon">📅</div>
1109 + <div class="stat-content">
1110 + <span class="stat-value"><?php echo esc_html($today_chats); ?></span>
1111 + <span class="stat-label"><?php esc_html_e('Today', 'mxchat'); ?></span>
1112 + </div>
1113 + </div>
1114 + <div class="mxchat-stat-card mxchat-stat-accent">
1115 + <div class="stat-icon">📆</div>
1116 + <div class="stat-content">
1117 + <span class="stat-value"><?php echo esc_html($week_chats); ?></span>
1118 + <span class="stat-label"><?php esc_html_e('Last 7 Days', 'mxchat'); ?></span>
1119 + </div>
1120 + </div>
1121 + <div class="mxchat-stat-card mxchat-stat-accent">
1122 + <div class="stat-icon">🗓️</div>
1123 + <div class="stat-content">
1124 + <span class="stat-value"><?php echo esc_html($month_chats); ?></span>
1125 + <span class="stat-label"><?php esc_html_e('Last 30 Days', 'mxchat'); ?></span>
1126 + </div>
1127 + </div>
1128 + </div>
1129 +
1130 + <div class="mxchat-chart-container">
1131 + <h3 class="mxchat-chart-title"><?php esc_html_e('7-Day Activity Trend', 'mxchat'); ?></h3>
1132 + <canvas id="mxchat-activity-chart"></canvas>
1133 + </div>
1134 + </div>
1135 + </div>
1136 +
1137 + <!-- Insights Tab -->
1138 + <div class="mxchat-metrics-panel" data-panel="insights">
1139 + <div class="mxchat-insights-grid">
1140 + <div class="mxchat-insight-card">
1141 + <div class="insight-header">
1142 + <span class="insight-icon">🕐</span>
1143 + <h3><?php esc_html_e('Peak Activity', 'mxchat'); ?></h3>
1144 + </div>
1145 + <div class="insight-content">
1146 + <div class="insight-value">
1147 + <?php
1148 + if ($busiest_hour) {
1149 + $hour = $busiest_hour->hour;
1150 + $formatted_hour = date('g A', strtotime("$hour:00"));
1151 + echo esc_html($formatted_hour);
1152 + } else {
1153 + echo esc_html__('N/A', 'mxchat');
1154 + }
1155 + ?>
1156 + </div>
1157 + <div class="insight-description">
1158 + <?php esc_html_e('Most active hour of the day', 'mxchat'); ?>
1159 + </div>
1160 + </div>
1161 + </div>
1162 +
1163 + <div class="mxchat-insight-card">
1164 + <div class="insight-header">
1165 + <span class="insight-icon">📊</span>
1166 + <h3><?php esc_html_e('Engagement Rate', 'mxchat'); ?></h3>
1167 + </div>
1168 + <div class="insight-content">
1169 + <div class="insight-value">
1170 + <?php
1171 + $engagement_rate = $total_chats > 0 ? round(($total_messages / $total_chats), 1) : 0;
1172 + echo esc_html($engagement_rate);
1173 + ?>
1174 + </div>
1175 + <div class="insight-description">
1176 + <?php esc_html_e('Messages exchanged per chat', 'mxchat'); ?>
1177 + </div>
1178 + </div>
1179 + </div>
1180 +
1181 + <div class="mxchat-insight-card">
1182 + <div class="insight-header">
1183 + <span class="insight-icon">👤</span>
1184 + <h3><?php esc_html_e('User Distribution', 'mxchat'); ?></h3>
1185 + </div>
1186 + <div class="insight-content">
1187 + <div class="insight-breakdown">
1188 + <div class="breakdown-item">
1189 + <span class="breakdown-label"><?php esc_html_e('Registered', 'mxchat'); ?></span>
1190 + <span class="breakdown-value">
1191 + <?php
1192 + $registered_pct = $total_users > 0 ? round(($registered_users / $total_users) * 100) : 0;
1193 + echo esc_html($registered_pct . '%');
1194 + ?>
1195 + </span>
1196 + </div>
1197 + <div class="breakdown-item">
1198 + <span class="breakdown-label"><?php esc_html_e('Guests', 'mxchat'); ?></span>
1199 + <span class="breakdown-value">
1200 + <?php
1201 + $guest_pct = $total_users > 0 ? round(($guest_users / $total_users) * 100) : 0;
1202 + echo esc_html($guest_pct . '%');
1203 + ?>
1204 + </span>
1205 + </div>
1206 + </div>
1207 + </div>
1208 + </div>
1209 +
1210 + <div class="mxchat-insight-card">
1211 + <div class="insight-header">
1212 + <span class="insight-icon">⚡</span>
1213 + <h3><?php esc_html_e('Activity Status', 'mxchat'); ?></h3>
1214 + </div>
1215 + <div class="insight-content">
1216 + <div class="insight-value insight-status">
1217 + <?php
1218 + if ($today_chats > 0) {
1219 + echo '<span class="status-badge status-active">' . esc_html__('Active', 'mxchat') . '</span>';
1220 + } else if ($week_chats > 0) {
1221 + echo '<span class="status-badge status-moderate">' . esc_html__('Moderate', 'mxchat') . '</span>';
1222 + } else {
1223 + echo '<span class="status-badge status-quiet">' . esc_html__('Quiet', 'mxchat') . '</span>';
1224 + }
1225 + ?>
1226 + </div>
1227 + <div class="insight-description">
1228 + <?php
1229 + if ($today_chats > 0) {
1230 + echo esc_html(sprintf(__('%d chats today', 'mxchat'), $today_chats));
1231 + } else if ($week_chats > 0) {
1232 + echo esc_html(sprintf(__('%d chats this week', 'mxchat'), $week_chats));
1233 + } else {
1234 + echo esc_html__('No recent activity', 'mxchat');
1235 + }
1236 + ?>
1237 + </div>
1238 + </div>
1239 + </div>
1240 + </div>
1241 + </div>
1242 + </div>
1243 + </div>
1244 +
1245 + <script>
1246 + var mxchatChartData = {
1247 + labels: <?php echo json_encode($chart_labels); ?>,
1248 + chats: <?php echo json_encode($chart_chats); ?>,
1249 + messages: <?php echo json_encode($chart_messages); ?>
1250 + };
1251 + </script>
1120 1252
1121 - // Prepare page data for the template
1122 - $page_data = array(
1123 - 'total_chats' => $total_chats,
1124 - 'total_messages' => $total_messages,
1125 - 'total_users' => $total_users,
1126 - 'registered_users' => $registered_users,
1127 - 'guest_users' => $guest_users,
1128 - 'agent_tests' => $agent_tests,
1129 - 'today_chats' => $today_chats,
1130 - 'week_chats' => $week_chats,
1131 - 'month_chats' => $month_chats,
1132 - 'avg_messages' => $avg_messages,
1133 - 'busiest_hour' => $busiest_hour,
1134 - 'chart_labels' => $chart_labels,
1135 - 'chart_chats' => $chart_chats,
1136 - 'chart_messages' => $chart_messages,
1137 - );
1253 + <!-- Search and Filter Controls with Notification Settings Button -->
1254 + <div class="mxchat-controls-wrapper">
1255 + <div class="mxchat-search-box">
1256 + <input type="text" id="mxchat-search-transcripts"
1257 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
1258 + class="regular-text">
1259 + </div>
1260 + <form id="mxchat-delete-form" method="post">
1261 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
1262 + <div class="mxchat-controls">
1263 + <button type="button" id="mxchat-chat-email-notification-btn" class="mxchat-action-button">
1264 + <span class="dashicons dashicons-admin-generic"></span>
1265 + <?php esc_html_e('Settings', 'mxchat'); ?>
1266 + </button>
1267 + <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
1268 + <span class="dashicons dashicons-download"></span>
1269 + <?php esc_html_e('Export All Chats', 'mxchat'); ?>
1270 + </button>
1271 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
1272 + <span class="dashicons dashicons-yes-alt"></span>
1273 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
1274 + </button>
1275 + <button type="submit" class="button delete-chats-button">
1276 + <span class="dashicons dashicons-trash"></span>
1277 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
1278 + </button>
1279 + </div>
1280 + </form>
1281 + </div>
1138 1282
1139 - // Include and render the new template
1140 - require_once plugin_dir_path(__FILE__) . 'admin-transcripts-page.php';
1141 - mxchat_render_transcripts_page($this, $page_data);
1283 + <!-- Transcripts Container -->
1284 + <div id="mxchat-transcripts"></div>
1285 + </div>
1286 + </div>
1287 +
1288 + <!-- Modal for Chat Email Notification Settings -->
1289 + <div id="mxchat-chat-email-notification-modal" class="mxchat-chat-notification-modal-overlay" style="display: none;">
1290 + <div class="mxchat-chat-notification-modal-content">
1291 + <div class="mxchat-chat-notification-modal-header">
1292 + <h2><?php esc_html_e('Transcript Settings', 'mxchat'); ?></h2>
1293 + <button type="button" class="mxchat-chat-notification-modal-close">&times;</button>
1294 + </div>
1295 + <div class="mxchat-chat-notification-modal-body">
1296 + <form method="post" action="options.php" id="mxchat-chat-email-notification-form">
1297 + <?php
1298 + settings_fields('mxchat_transcripts_options');
1299 + do_settings_sections('mxchat-transcripts');
1300 + ?>
1301 + <div class="mxchat-chat-notification-modal-footer">
1302 + <button type="submit" class="button button-primary">
1303 + <?php esc_html_e('Save Settings', 'mxchat'); ?>
1304 + </button>
1305 + <button type="button" class="button mxchat-chat-notification-modal-cancel">
1306 + <?php esc_html_e('Cancel', 'mxchat'); ?>
1307 + </button>
1308 + </div>
1309 + </form>
1310 + </div>
1311 + </div>
1312 + </div>
1313 + <?php
1142 1314 }
1143 1315
1144 -/**
1145 - * Get chart data for transcripts page
1146 - * Used by both page render and script localization
1147 - *
1148 - * @return array Chart data with labels, chats, and messages arrays
1149 - */
1150 -private function get_transcripts_chart_data() {
1151 - global $wpdb;
1152 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1153 1316
1154 - // Get daily chat data for last 7 days - same query as mxchat_transcripts_page()
1155 - $daily_stats = $wpdb->get_results("
1156 - SELECT
1157 - DATE(timestamp) as date,
1158 - COUNT(DISTINCT session_id) as chats,
1159 - COUNT(*) as messages
1160 - FROM $table_name
1161 - WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)
1162 - GROUP BY DATE(timestamp)
1163 - ORDER BY date ASC
1164 - ");
1165 -
1166 - // Prepare chart data
1167 - $chart_labels = array();
1168 - $chart_chats = array();
1169 - $chart_messages = array();
1170 -
1171 - // Fill last 7 days with data - same logic as mxchat_transcripts_page()
1172 - for ($i = 6; $i >= 0; $i--) {
1173 - $date = date('Y-m-d', strtotime("-$i days"));
1174 - $day_name = date('D', strtotime("-$i days"));
1175 - $chart_labels[] = $day_name;
1176 -
1177 - $found = false;
1178 - if ($daily_stats) {
1179 - foreach ($daily_stats as $stat) {
1180 - if ($stat->date === $date) {
1181 - $chart_chats[] = (int)$stat->chats;
1182 - $chart_messages[] = (int)$stat->messages;
1183 - $found = true;
1184 - break;
1185 - }
1186 - }
1187 - }
1188 - if (!$found) {
1189 - $chart_chats[] = 0;
1190 - $chart_messages[] = 0;
1191 - }
1192 - }
1193 -
1194 - return array(
1195 - 'labels' => $chart_labels,
1196 - 'chats' => $chart_chats,
1197 - 'messages' => $chart_messages
1198 - );
1199 -}
1200 -
1201 1317 public function mxchat_transcripts_notification_section_callback() {
1202 1318 echo '<p>' . esc_html__('Configure email notifications for new chat transcripts. You will receive an email notification when a new chat session begins.', 'mxchat') . '</p>';
1203 1319 }
1204 1320 public function mxchat_enable_notifications_callback() {
@@ -1406,43 +1522,23 @@
1406 1522 }
1407 1523
1408 1524 global $wpdb;
1409 1525 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1410 - $translations_table = $wpdb->prefix . 'mxchat_transcript_translations';
1411 -
1526 +
1412 1527 // Calculate the cutoff timestamp
1413 1528 $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
1414 -
1415 - // First, get the session IDs that will be deleted (to clean up translations too)
1416 - $sessions_to_delete = $wpdb->get_col(
1417 - $wpdb->prepare(
1418 - "SELECT DISTINCT session_id FROM {$table_name} WHERE timestamp < %s",
1419 - $cutoff_date
1420 - )
1421 - );
1422 -
1529 +
1423 1530 // Delete transcripts older than the cutoff date
1424 1531 $deleted = $wpdb->query(
1425 1532 $wpdb->prepare(
1426 - "DELETE FROM {$table_name} WHERE timestamp < %s",
1533 + "DELETE FROM {$table_name} WHERE created_at < %s",
1427 1534 $cutoff_date
1428 1535 )
1429 1536 );
1430 -
1431 - // Also delete any translations for the deleted sessions
1432 - if (!empty($sessions_to_delete) && $wpdb->get_var("SHOW TABLES LIKE '$translations_table'") === $translations_table) {
1433 - $placeholders = implode(',', array_fill(0, count($sessions_to_delete), '%s'));
1434 - $wpdb->query(
1435 - $wpdb->prepare(
1436 - "DELETE FROM {$translations_table} WHERE session_id IN ($placeholders)",
1437 - $sessions_to_delete
1438 - )
1439 - );
1440 - }
1441 -
1537 +
1442 1538 // Log the cleanup action
1443 1539 if ($deleted !== false && $deleted > 0) {
1444 - //error_log(sprintf('MXChat: Auto-deleted %d transcripts older than %d days', $deleted, $days));
1540 + error_log(sprintf('MXChat: Auto-deleted %d transcripts older than %d days', $deleted, $days));
1445 1541 }
1446 1542 }
1447 1543
1448 1544 public function export_chat_transcripts() {
@@ -1504,482 +1600,9 @@
1504 1600 fclose($output);
1505 1601 wp_die();
1506 1602 }
1507 1603
1508 -/**
1509 - * Handle translation of chat messages via AJAX
1510 - */
1511 -public function mxchat_translate_messages() {
1512 - if (!current_user_can('manage_options')) {
1513 - wp_send_json_error(['error' => 'Insufficient permissions']);
1514 - wp_die();
1515 - }
1516 1604
1517 - $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
1518 - $target_lang = isset($_POST['target_lang']) ? sanitize_text_field($_POST['target_lang']) : 'en';
1519 - $messages_json = isset($_POST['messages']) ? wp_unslash($_POST['messages']) : '[]';
1520 - $messages = json_decode($messages_json, true);
1521 -
1522 - if (empty($session_id)) {
1523 - wp_send_json_error(['error' => 'No session ID provided']);
1524 - wp_die();
1525 - }
1526 -
1527 - if (empty($messages) || !is_array($messages)) {
1528 - wp_send_json_error(['error' => 'No messages to translate']);
1529 - wp_die();
1530 - }
1531 -
1532 - // Language names for prompting
1533 - $languages = [
1534 - 'en' => 'English',
1535 - 'es' => 'Spanish',
1536 - 'fr' => 'French',
1537 - 'de' => 'German',
1538 - 'it' => 'Italian',
1539 - 'pt' => 'Portuguese',
1540 - 'nl' => 'Dutch',
1541 - 'ru' => 'Russian',
1542 - 'zh' => 'Chinese',
1543 - 'ja' => 'Japanese',
1544 - 'ko' => 'Korean',
1545 - 'ar' => 'Arabic',
1546 - 'hi' => 'Hindi',
1547 - 'tr' => 'Turkish',
1548 - 'pl' => 'Polish',
1549 - 'vi' => 'Vietnamese',
1550 - 'th' => 'Thai',
1551 - 'id' => 'Indonesian',
1552 - 'sv' => 'Swedish',
1553 - 'da' => 'Danish'
1554 - ];
1555 -
1556 - $target_lang_name = isset($languages[$target_lang]) ? $languages[$target_lang] : 'English';
1557 -
1558 - // Build combined text for translation (numbered for parsing)
1559 - $numbered_messages = [];
1560 - foreach ($messages as $i => $msg) {
1561 - $content = isset($msg['content']) ? trim($msg['content']) : '';
1562 - if (!empty($content)) {
1563 - $numbered_messages[] = "[MSG" . $i . "]" . $content . "[/MSG" . $i . "]";
1564 - }
1565 - }
1566 -
1567 - if (empty($numbered_messages)) {
1568 - wp_send_json_error(['error' => 'No valid messages to translate']);
1569 - wp_die();
1570 - }
1571 -
1572 - $combined_text = implode("\n\n", $numbered_messages);
1573 -
1574 - // Prepare the translation prompt
1575 - $system_prompt = "You are a translator. Translate the following messages to {$target_lang_name}. Keep the [MSG#] and [/MSG#] tags exactly as they are - only translate the content between them. Maintain the original formatting, line breaks, and any HTML tags. Return ONLY the translated messages with the tags, no explanations.";
1576 -
1577 - // Get user's selected model and determine provider
1578 - $options = get_option('mxchat_options', []);
1579 - $selected_model = $options['model'] ?? 'gpt-5.1-chat-latest';
1580 -
1581 - // Check if using OpenRouter
1582 - if ($selected_model === 'openrouter') {
1583 - $provider = 'openrouter';
1584 - $selected_model = $options['openrouter_selected_model'] ?? '';
1585 - $api_key = $options['openrouter_api_key'] ?? '';
1586 -
1587 - if (empty($selected_model)) {
1588 - wp_send_json_error(['error' => 'No OpenRouter model selected']);
1589 - wp_die();
1590 - }
1591 - } else {
1592 - // Determine provider from model name
1593 - $model_parts = explode('-', $selected_model);
1594 - $provider = strtolower($model_parts[0]);
1595 -
1596 - // Get the appropriate API key based on provider
1597 - $api_key = '';
1598 - switch ($provider) {
1599 - case 'gpt':
1600 - case 'o1':
1601 - $api_key = $options['api_key'] ?? '';
1602 - break;
1603 - case 'claude':
1604 - $api_key = $options['claude_api_key'] ?? '';
1605 - break;
1606 - case 'grok':
1607 - $api_key = $options['xai_api_key'] ?? '';
1608 - break;
1609 - case 'deepseek':
1610 - $api_key = $options['deepseek_api_key'] ?? '';
1611 - break;
1612 - case 'gemini':
1613 - $api_key = $options['gemini_api_key'] ?? '';
1614 - break;
1615 - default:
1616 - // Default to OpenAI
1617 - $api_key = $options['api_key'] ?? '';
1618 - $provider = 'gpt';
1619 - break;
1620 - }
1621 - }
1622 -
1623 - if (empty($api_key)) {
1624 - wp_send_json_error(['error' => 'No API key configured for ' . $provider]);
1625 - wp_die();
1626 - }
1627 -
1628 - // Make API request based on provider
1629 - $response = $this->translate_with_provider($provider, $selected_model, $api_key, $system_prompt, $combined_text);
1630 -
1631 - if (is_wp_error($response)) {
1632 - wp_send_json_error(['error' => $response->get_error_message()]);
1633 - wp_die();
1634 - }
1635 -
1636 - // Parse the response to extract translated messages
1637 - $translations = [];
1638 - foreach ($messages as $msg) {
1639 - $index = $msg['index'];
1640 - $pattern = '/\[MSG' . $index . '\](.*?)\[\/MSG' . $index . '\]/s';
1641 - if (preg_match($pattern, $response, $matches)) {
1642 - $translations[] = [
1643 - 'index' => $index,
1644 - 'translated' => trim($matches[1])
1645 - ];
1646 - }
1647 - }
1648 -
1649 - // Save translations to database
1650 - if (!empty($translations)) {
1651 - $this->save_transcript_translation($session_id, $target_lang, $translations);
1652 - }
1653 -
1654 - wp_send_json(['success' => true, 'translations' => $translations, 'language' => $target_lang]);
1655 - wp_die();
1656 -}
1657 -
1658 -/**
1659 - * Save transcript translation to database
1660 - */
1661 -private function save_transcript_translation($session_id, $language_code, $translations) {
1662 - global $wpdb;
1663 - $table_name = $wpdb->prefix . 'mxchat_transcript_translations';
1664 -
1665 - // Check if table exists, create if not
1666 - if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
1667 - mxchat_create_translations_table();
1668 - }
1669 -
1670 - $now = current_time('mysql');
1671 - $translations_json = wp_json_encode($translations);
1672 -
1673 - // Use REPLACE to insert or update
1674 - $wpdb->query($wpdb->prepare(
1675 - "REPLACE INTO $table_name (session_id, language_code, translations, created_at, updated_at)
1676 - VALUES (%s, %s, %s, %s, %s)",
1677 - $session_id,
1678 - $language_code,
1679 - $translations_json,
1680 - $now,
1681 - $now
1682 - ));
1683 -}
1684 -
1685 -/**
1686 - * Get saved translation for a session
1687 - */
1688 -public function mxchat_get_transcript_translation() {
1689 - if (!current_user_can('manage_options')) {
1690 - wp_send_json_error(['error' => 'Insufficient permissions']);
1691 - wp_die();
1692 - }
1693 -
1694 - $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
1695 -
1696 - if (empty($session_id)) {
1697 - wp_send_json_error(['error' => 'No session ID provided']);
1698 - wp_die();
1699 - }
1700 -
1701 - global $wpdb;
1702 - $table_name = $wpdb->prefix . 'mxchat_transcript_translations';
1703 -
1704 - // Check if table exists
1705 - if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") !== $table_name) {
1706 - wp_send_json(['success' => true, 'has_translation' => false]);
1707 - wp_die();
1708 - }
1709 -
1710 - // Get the most recent translation for this session
1711 - $result = $wpdb->get_row($wpdb->prepare(
1712 - "SELECT language_code, translations FROM $table_name WHERE session_id = %s ORDER BY updated_at DESC LIMIT 1",
1713 - $session_id
1714 - ));
1715 -
1716 - if ($result) {
1717 - $translations = json_decode($result->translations, true);
1718 - wp_send_json([
1719 - 'success' => true,
1720 - 'has_translation' => true,
1721 - 'language' => $result->language_code,
1722 - 'translations' => $translations
1723 - ]);
1724 - } else {
1725 - wp_send_json(['success' => true, 'has_translation' => false]);
1726 - }
1727 - wp_die();
1728 -}
1729 -
1730 -/**
1731 - * Translate text using the user's selected provider and model
1732 - */
1733 -private function translate_with_provider($provider, $model, $api_key, $system_prompt, $text) {
1734 - switch ($provider) {
1735 - case 'claude':
1736 - return $this->translate_with_claude($api_key, $model, $system_prompt, $text);
1737 - case 'grok':
1738 - return $this->translate_with_xai($api_key, $model, $system_prompt, $text);
1739 - case 'deepseek':
1740 - return $this->translate_with_deepseek($api_key, $model, $system_prompt, $text);
1741 - case 'gemini':
1742 - return $this->translate_with_gemini($api_key, $model, $system_prompt, $text);
1743 - case 'openrouter':
1744 - return $this->translate_with_openrouter($api_key, $model, $system_prompt, $text);
1745 - case 'gpt':
1746 - case 'o1':
1747 - default:
1748 - return $this->translate_with_openai($api_key, $model, $system_prompt, $text);
1749 - }
1750 -}
1751 -
1752 -/**
1753 - * Translate text using OpenAI API
1754 - */
1755 -private function translate_with_openai($api_key, $model, $system_prompt, $text) {
1756 - $response = wp_remote_post('https://api.openai.com/v1/chat/completions', [
1757 - 'timeout' => 60,
1758 - 'headers' => [
1759 - 'Authorization' => 'Bearer ' . $api_key,
1760 - 'Content-Type' => 'application/json'
1761 - ],
1762 - 'body' => wp_json_encode([
1763 - 'model' => $model,
1764 - 'messages' => [
1765 - ['role' => 'system', 'content' => $system_prompt],
1766 - ['role' => 'user', 'content' => $text]
1767 - ],
1768 - 'temperature' => 0.3
1769 - ])
1770 - ]);
1771 -
1772 - if (is_wp_error($response)) {
1773 - return $response;
1774 - }
1775 -
1776 - $body = json_decode(wp_remote_retrieve_body($response), true);
1777 -
1778 - if (isset($body['error'])) {
1779 - return new WP_Error('api_error', $body['error']['message']);
1780 - }
1781 -
1782 - if (isset($body['choices'][0]['message']['content'])) {
1783 - return $body['choices'][0]['message']['content'];
1784 - }
1785 -
1786 - return new WP_Error('api_error', 'Invalid API response');
1787 -}
1788 -
1789 -/**
1790 - * Translate text using Claude API
1791 - */
1792 -private function translate_with_claude($api_key, $model, $system_prompt, $text) {
1793 - $response = wp_remote_post('https://api.anthropic.com/v1/messages', [
1794 - 'timeout' => 60,
1795 - 'headers' => [
1796 - 'x-api-key' => $api_key,
1797 - 'anthropic-version' => '2023-06-01',
1798 - 'Content-Type' => 'application/json'
1799 - ],
1800 - 'body' => wp_json_encode([
1801 - 'model' => $model,
1802 - 'max_tokens' => 4096,
1803 - 'system' => $system_prompt,
1804 - 'messages' => [
1805 - ['role' => 'user', 'content' => $text]
1806 - ]
1807 - ])
1808 - ]);
1809 -
1810 - if (is_wp_error($response)) {
1811 - return $response;
1812 - }
1813 -
1814 - $body = json_decode(wp_remote_retrieve_body($response), true);
1815 -
1816 - if (isset($body['error'])) {
1817 - return new WP_Error('api_error', $body['error']['message']);
1818 - }
1819 -
1820 - if (isset($body['content'][0]['text'])) {
1821 - return $body['content'][0]['text'];
1822 - }
1823 -
1824 - return new WP_Error('api_error', 'Invalid API response');
1825 -}
1826 -
1827 -/**
1828 - * Translate text using xAI (Grok) API
1829 - */
1830 -private function translate_with_xai($api_key, $model, $system_prompt, $text) {
1831 - $response = wp_remote_post('https://api.x.ai/v1/chat/completions', [
1832 - 'timeout' => 60,
1833 - 'headers' => [
1834 - 'Authorization' => 'Bearer ' . $api_key,
1835 - 'Content-Type' => 'application/json'
1836 - ],
1837 - 'body' => wp_json_encode([
1838 - 'model' => $model,
1839 - 'messages' => [
1840 - ['role' => 'system', 'content' => $system_prompt],
1841 - ['role' => 'user', 'content' => $text]
1842 - ],
1843 - 'temperature' => 0.3
1844 - ])
1845 - ]);
1846 -
1847 - if (is_wp_error($response)) {
1848 - return $response;
1849 - }
1850 -
1851 - $body = json_decode(wp_remote_retrieve_body($response), true);
1852 -
1853 - if (isset($body['error'])) {
1854 - return new WP_Error('api_error', $body['error']['message']);
1855 - }
1856 -
1857 - if (isset($body['choices'][0]['message']['content'])) {
1858 - return $body['choices'][0]['message']['content'];
1859 - }
1860 -
1861 - return new WP_Error('api_error', 'Invalid API response');
1862 -}
1863 -
1864 -/**
1865 - * Translate text using DeepSeek API
1866 - */
1867 -private function translate_with_deepseek($api_key, $model, $system_prompt, $text) {
1868 - $response = wp_remote_post('https://api.deepseek.com/v1/chat/completions', [
1869 - 'timeout' => 60,
1870 - 'headers' => [
1871 - 'Authorization' => 'Bearer ' . $api_key,
1872 - 'Content-Type' => 'application/json'
1873 - ],
1874 - 'body' => wp_json_encode([
1875 - 'model' => $model,
1876 - 'messages' => [
1877 - ['role' => 'system', 'content' => $system_prompt],
1878 - ['role' => 'user', 'content' => $text]
1879 - ],
1880 - 'temperature' => 0.3
1881 - ])
1882 - ]);
1883 -
1884 - if (is_wp_error($response)) {
1885 - return $response;
1886 - }
1887 -
1888 - $body = json_decode(wp_remote_retrieve_body($response), true);
1889 -
1890 - if (isset($body['error'])) {
1891 - return new WP_Error('api_error', $body['error']['message']);
1892 - }
1893 -
1894 - if (isset($body['choices'][0]['message']['content'])) {
1895 - return $body['choices'][0]['message']['content'];
1896 - }
1897 -
1898 - return new WP_Error('api_error', 'Invalid API response');
1899 -}
1900 -
1901 -/**
1902 - * Translate text using Google Gemini API
1903 - */
1904 -private function translate_with_gemini($api_key, $model, $system_prompt, $text) {
1905 - $url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model . ':generateContent?key=' . $api_key;
1906 -
1907 - $response = wp_remote_post($url, [
1908 - 'timeout' => 60,
1909 - 'headers' => [
1910 - 'Content-Type' => 'application/json'
1911 - ],
1912 - 'body' => wp_json_encode([
1913 - 'contents' => [
1914 - [
1915 - 'parts' => [
1916 - ['text' => $system_prompt . "\n\n" . $text]
1917 - ]
1918 - ]
1919 - ],
1920 - 'generationConfig' => [
1921 - 'temperature' => 0.3
1922 - ]
1923 - ])
1924 - ]);
1925 -
1926 - if (is_wp_error($response)) {
1927 - return $response;
1928 - }
1929 -
1930 - $body = json_decode(wp_remote_retrieve_body($response), true);
1931 -
1932 - if (isset($body['error'])) {
1933 - return new WP_Error('api_error', $body['error']['message']);
1934 - }
1935 -
1936 - if (isset($body['candidates'][0]['content']['parts'][0]['text'])) {
1937 - return $body['candidates'][0]['content']['parts'][0]['text'];
1938 - }
1939 -
1940 - return new WP_Error('api_error', 'Invalid API response');
1941 -}
1942 -
1943 -/**
1944 - * Translate text using OpenRouter API
1945 - */
1946 -private function translate_with_openrouter($api_key, $model, $system_prompt, $text) {
1947 - $response = wp_remote_post('https://openrouter.ai/api/v1/chat/completions', [
1948 - 'timeout' => 60,
1949 - 'headers' => [
1950 - 'Authorization' => 'Bearer ' . $api_key,
1951 - 'Content-Type' => 'application/json',
1952 - 'HTTP-Referer' => home_url(),
1953 - 'X-Title' => 'MxChat Translation'
1954 - ],
1955 - 'body' => wp_json_encode([
1956 - 'model' => $model,
1957 - 'messages' => [
1958 - ['role' => 'system', 'content' => $system_prompt],
1959 - ['role' => 'user', 'content' => $text]
1960 - ],
1961 - 'temperature' => 0.3
1962 - ])
1963 - ]);
1964 -
1965 - if (is_wp_error($response)) {
1966 - return $response;
1967 - }
1968 -
1969 - $body = json_decode(wp_remote_retrieve_body($response), true);
1970 -
1971 - if (isset($body['error'])) {
1972 - return new WP_Error('api_error', $body['error']['message']);
1973 - }
1974 -
1975 - if (isset($body['choices'][0]['message']['content'])) {
1976 - return $body['choices'][0]['message']['content'];
1977 - }
1978 -
1979 - return new WP_Error('api_error', 'Invalid API response');
1980 -}
1981 -
1982 1605 public function mxchat_fetch_chat_history() {
1983 1606 global $wpdb;
1984 1607 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1985 1608 $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
@@ -1984,10 +1607,9 @@
1984 1607 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1985 1608 $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1986 1609
1987 1610 if (!current_user_can('manage_options')) {
1988 - wp_send_json_error(['message' => 'Insufficient permissions']);
1989 - wp_die();
1611 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1990 1612 }
1991 1613
1992 1614 $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1993 1615 $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
@@ -1992,13 +1614,12 @@
1992 1614 $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1993 1615 $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
1994 1616 $offset = ($page - 1) * $per_page;
1995 1617 $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
1996 - $sort_order = isset($_POST['sort_order']) && $_POST['sort_order'] === 'asc' ? 'ASC' : 'DESC';
1997 1618
1998 - // Build search condition
1999 1619 $search_condition = '';
2000 1620 $search_params = [];
1621 +
2001 1622 if (!empty($search)) {
2002 1623 $search_condition = "WHERE (
2003 1624 session_id LIKE %s
2004 1625 OR user_email LIKE %s
@@ -2008,51 +1629,52 @@
2008 1629 )";
2009 1630 $search_params = array_fill(0, 5, '%' . $wpdb->esc_like($search) . '%');
2010 1631 }
2011 1632
2012 - // Get total count
2013 1633 $count_query = !empty($search)
2014 1634 ? $wpdb->prepare("SELECT COUNT(DISTINCT session_id) FROM {$table_name} {$search_condition}", $search_params)
2015 1635 : "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
2016 - $total_sessions = (int) $wpdb->get_var($count_query);
2017 1636
2018 - // Get session IDs for current page (sorted by newest or oldest)
1637 + $total_sessions = $wpdb->get_var($count_query);
1638 +
2019 1639 $session_query = !empty($search)
2020 1640 ? $wpdb->prepare(
2021 1641 "SELECT DISTINCT t.session_id FROM {$table_name} t {$search_condition}
2022 - GROUP BY t.session_id ORDER BY MAX(t.timestamp) {$sort_order} LIMIT %d OFFSET %d",
1642 + GROUP BY t.session_id ORDER BY MAX(t.timestamp) DESC LIMIT %d OFFSET %d",
2023 1643 array_merge($search_params, [$per_page, $offset])
2024 1644 )
2025 1645 : $wpdb->prepare(
2026 1646 "SELECT DISTINCT session_id FROM {$table_name}
2027 - GROUP BY session_id ORDER BY MAX(timestamp) {$sort_order} LIMIT %d OFFSET %d",
1647 + GROUP BY session_id ORDER BY MAX(timestamp) DESC LIMIT %d OFFSET %d",
2028 1648 $per_page, $offset
2029 1649 );
1650 +
2030 1651 $session_ids = $wpdb->get_col($session_query);
2031 1652
2032 - $total_pages = ceil($total_sessions / $per_page);
2033 -
2034 1653 if (empty($session_ids)) {
1654 + ob_start();
1655 + echo '<div class="mxchat-no-results">';
1656 + echo esc_html__('No chat history found.', 'mxchat');
1657 + if (!empty($search)) {
1658 + echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
1659 + }
1660 + echo '</div>';
2035 1661 wp_send_json([
2036 - 'success' => true,
2037 - 'sessions' => [],
1662 + 'html' => ob_get_clean(),
2038 1663 'page' => $page,
2039 1664 'total_pages' => 0,
2040 - 'total_sessions' => 0,
2041 - 'showing_start' => 0,
2042 - 'showing_end' => 0
1665 + 'total_sessions' => 0
2043 1666 ]);
2044 1667 wp_die();
2045 1668 }
2046 1669
2047 - // Check for optional columns/tables
2048 1670 $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
2049 1671 $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
2050 1672
2051 - // Build session list data
2052 - $sessions = [];
1673 + ob_start();
1674 + echo '<div class="mxchat-transcript">';
1675 +
2053 1676 foreach ($session_ids as $session_id) {
2054 - // Get session metadata
2055 1677 $session_data = $originating_columns_exist
2056 1678 ? $wpdb->get_row($wpdb->prepare(
2057 1679 "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
2058 1680 FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
@@ -2063,217 +1685,186 @@
2063 1685 WHERE session_id = %s LIMIT 1",
2064 1686 $session_id
2065 1687 ));
2066 1688
2067 - // Get message count and latest timestamp
2068 - $message_stats = $wpdb->get_row($wpdb->prepare(
2069 - "SELECT COUNT(*) as count, MAX(timestamp) as latest FROM {$table_name} WHERE session_id = %s",
2070 - $session_id
2071 - ));
1689 + $clicked_urls = [];
1690 + if ($url_table_exists) {
1691 + $url_clicks = $wpdb->get_results(
1692 + $wpdb->prepare(
1693 + "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
1694 + WHERE session_id = %s ORDER BY click_timestamp ASC",
1695 + $session_id
1696 + )
1697 + );
1698 + foreach ($url_clicks as $click) {
1699 + $clicked_urls[] = $click->clicked_url;
1700 + }
1701 + }
2072 1702
2073 - // Get first user message as preview
2074 - $first_user_msg = $wpdb->get_var($wpdb->prepare(
2075 - "SELECT message FROM {$table_name} WHERE session_id = %s AND role = 'user' ORDER BY timestamp ASC LIMIT 1",
2076 - $session_id
2077 - ));
2078 - $preview = $first_user_msg ? wp_trim_words(wp_strip_all_tags(stripslashes($first_user_msg)), 12, '...') : 'No messages';
1703 + $messages = $wpdb->get_results(
1704 + $wpdb->prepare(
1705 + "SELECT * FROM {$table_name}
1706 + WHERE session_id = %s ORDER BY timestamp ASC",
1707 + $session_id
1708 + )
1709 + );
2079 1710
2080 - // Build display name
1711 + $latest_timestamp = !empty($messages) ? end($messages)->timestamp : $session_data->timestamp;
1712 + $session_time = wp_date('M j, g:ia', strtotime($latest_timestamp . ' UTC'));
1713 +
2081 1714 $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
2082 - $user_name = !empty($session_data->user_name) ? $session_data->user_name : '';
1715 + $user_name = !empty($session_data->user_name) ? $session_data->user_name : ''; // NEW
2083 1716 $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
2084 1717
2085 - $display_name = $user_name ?: ($user_email ? explode('@', $user_email)[0] : $user_identifier);
2086 - $display_sub = $user_email ?: ('ID: ' . $user_identifier);
2087 -
2088 - // Format time - show relative for recent, date for older
2089 - $timestamp = strtotime($message_stats->latest . ' UTC');
2090 - $now = time();
2091 - $diff = $now - $timestamp;
2092 - if ($diff < 3600) {
2093 - $time_display = floor($diff / 60) . 'm ago';
2094 - } elseif ($diff < 86400) {
2095 - $time_display = floor($diff / 3600) . 'h ago';
2096 - } elseif ($diff < 604800) {
2097 - $time_display = floor($diff / 86400) . 'd ago';
1718 + // Build user display with name if available
1719 + if (!empty($user_email)) {
1720 + $user_display = $user_email;
1721 + if (!empty($user_name)) {
1722 + $user_display .= ' (' . $user_name . ')';
1723 + }
2098 1724 } else {
2099 - $time_display = wp_date('M j', $timestamp);
1725 + $user_display = $user_identifier;
2100 1726 }
1727 + echo '<div class="mxchat-session">';
1728 + echo '<div class="mxchat-session-header" data-session-id="' . esc_attr($session_id) . '">';
1729 + echo '<div class="mxchat-user-row">' . esc_html($user_display) . '</div>';
2101 1730
2102 - // Get initials for avatar
2103 - $initials = strtoupper(substr($display_name, 0, 2));
2104 - if (strlen($display_name) > 2 && strpos($display_name, ' ') !== false) {
2105 - $parts = explode(' ', $display_name);
2106 - $initials = strtoupper(substr($parts[0], 0, 1) . substr(end($parts), 0, 1));
1731 + if ($originating_columns_exist && !empty($session_data->originating_page_url)) {
1732 + $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : $session_data->originating_page_url;
1733 +
1734 + echo '<div class="mxchat-header-row flex-between">';
1735 + echo '<div class="mxchat-main-info">';
1736 + echo '<a href="' . esc_url($session_data->originating_page_url) . '" target="_blank" class="mxchat-page-link">';
1737 + echo esc_html($page_title);
1738 + echo '</a>';
1739 + echo '<span class="mxchat-session-time">' . esc_html($session_time) . '</span>';
1740 + echo '</div>';
1741 + echo '</div>';
2107 1742 }
2108 1743
2109 - $sessions[] = [
2110 - 'session_id' => $session_id,
2111 - 'display_name' => $display_name,
2112 - 'display_sub' => $display_sub,
2113 - 'initials' => $initials,
2114 - 'preview' => $preview,
2115 - 'message_count' => (int) $message_stats->count,
2116 - 'time_display' => $time_display,
2117 - 'timestamp' => $message_stats->latest
2118 - ];
2119 - }
1744 + if (!empty($clicked_urls)) {
1745 + echo '<div class="mxchat-links-row">';
1746 + echo '<span class="mxchat-label">Clicked:</span> ';
1747 + $link_count = 0;
1748 + foreach ($clicked_urls as $url) {
1749 + if ($link_count > 0) echo ', ';
1750 + echo '<a href="' . esc_url($url) . '" target="_blank" class="mxchat-link-item">' . esc_html(parse_url($url, PHP_URL_HOST)) . '</a>';
1751 + $link_count++;
1752 + if ($link_count >= 3) {
1753 + if (count($clicked_urls) > 3) {
1754 + echo ' +' . (count($clicked_urls) - 3) . ' more';
1755 + }
1756 + break;
1757 + }
1758 + }
1759 + echo '</div>';
1760 + }
2120 1761
2121 - wp_send_json([
2122 - 'success' => true,
2123 - 'sessions' => $sessions,
2124 - 'page' => $page,
2125 - 'total_pages' => $total_pages,
2126 - 'total_sessions' => $total_sessions,
2127 - 'showing_start' => $offset + 1,
2128 - 'showing_end' => min($offset + $per_page, $total_sessions)
2129 - ]);
2130 - wp_die();
2131 -}
1762 + echo '</div>'; // end session-header
1763 + echo '<div class="mxchat-messages">';
2132 1764
2133 -/**
2134 - * Fetch single conversation details for split-panel view
2135 - */
2136 -public function mxchat_fetch_conversation() {
2137 - global $wpdb;
2138 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2139 - $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1765 + foreach ($messages as $transcript) {
1766 + $formatted_timestamp = wp_date('F j, Y g:i a', strtotime($transcript->timestamp . ' UTC'));
2140 1767
2141 - if (!current_user_can('manage_options')) {
2142 - wp_send_json_error(['message' => 'Insufficient permissions']);
2143 - wp_die();
2144 - }
1768 + switch ($transcript->role) {
1769 + case 'assistant':
1770 + case 'bot':
1771 + $message_class = 'bot-message';
1772 + $display_role = esc_html__('Chatbot', 'mxchat');
1773 + break;
1774 + case 'user':
1775 + $message_class = 'user-message';
1776 + $display_role = !empty($transcript->user_identifier)
1777 + ? sanitize_text_field($transcript->user_identifier)
1778 + : esc_html__('User', 'mxchat');
1779 + break;
1780 + case 'agent':
1781 + $message_class = 'agent-message';
1782 + $display_role = esc_html__('Agent', 'mxchat');
1783 + break;
1784 + default:
1785 + $message_class = 'unknown-message';
1786 + $display_role = esc_html__('Unknown', 'mxchat');
1787 + }
2145 1788
2146 - $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';
2147 - if (empty($session_id)) {
2148 - wp_send_json_error(['message' => 'No session ID provided']);
2149 - wp_die();
2150 - }
1789 + $message_content = wp_kses(
1790 + stripslashes($transcript->message),
1791 + [
1792 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1793 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1794 + 'a' => ['href' => [], 'title' => [], 'target' => []]
1795 + ]
1796 + );
1797 + $message_content = nl2br($message_content);
2151 1798
2152 - // Check for optional columns/tables
2153 - $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
2154 - $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
1799 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1800 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1801 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1802 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1803 + echo '</div>';
1804 + }
2155 1805
2156 - // Get session metadata
2157 - $session_data = $originating_columns_exist
2158 - ? $wpdb->get_row($wpdb->prepare(
2159 - "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
2160 - FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
2161 - $session_id
2162 - ))
2163 - : $wpdb->get_row($wpdb->prepare(
2164 - "SELECT user_email, user_name, user_identifier, timestamp FROM {$table_name}
2165 - WHERE session_id = %s LIMIT 1",
2166 - $session_id
2167 - ));
2168 -
2169 - if (!$session_data) {
2170 - wp_send_json_error(['message' => 'Session not found']);
2171 - wp_die();
1806 + echo '</div></div>'; // end messages and session
2172 1807 }
2173 1808
2174 - // Get clicked URLs
2175 - $clicked_urls = [];
2176 - if ($url_table_exists) {
2177 - $url_clicks = $wpdb->get_results($wpdb->prepare(
2178 - "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
2179 - WHERE session_id = %s ORDER BY click_timestamp ASC",
2180 - $session_id
2181 - ));
2182 - foreach ($url_clicks as $click) {
2183 - $clicked_urls[] = $click->clicked_url;
2184 - }
2185 - }
1809 + echo '</div>'; // end transcript
2186 1810
2187 - // Get all messages
2188 - $messages = $wpdb->get_results($wpdb->prepare(
2189 - "SELECT * FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC",
2190 - $session_id
2191 - ));
1811 + $total_pages = ceil($total_sessions / $per_page);
2192 1812
2193 - // Build user info
2194 - $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
2195 - $user_name = !empty($session_data->user_name) ? $session_data->user_name : '';
2196 - $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
1813 + echo '<div class="mxchat-pagination">';
1814 + echo '<div class="mxchat-pagination-info">';
1815 + echo sprintf(
1816 + esc_html__('Showing %1$d to %2$d of %3$d sessions', 'mxchat'),
1817 + $offset + 1,
1818 + min($offset + $per_page, $total_sessions),
1819 + $total_sessions
1820 + );
1821 + echo '</div>';
2197 1822
2198 - $display_name = $user_name ?: ($user_email ? explode('@', $user_email)[0] : $user_identifier);
2199 - $display_sub = $user_email ?: ('ID: ' . $user_identifier);
1823 + if ($total_pages > 1) {
1824 + echo '<div class="mxchat-pagination-controls">';
1825 + if ($page > 1) {
1826 + echo '<button class="mxchat-pagination-button" data-page="' . ($page - 1) . '">&laquo; ' . esc_html__('Previous', 'mxchat') . '</button>';
1827 + }
2200 1828
2201 - // Get initials
2202 - $initials = strtoupper(substr($display_name, 0, 2));
2203 - if (strlen($display_name) > 2 && strpos($display_name, ' ') !== false) {
2204 - $parts = explode(' ', $display_name);
2205 - $initials = strtoupper(substr($parts[0], 0, 1) . substr(end($parts), 0, 1));
2206 - }
1829 + $start_page = max(1, $page - 2);
1830 + $end_page = min($total_pages, $page + 2);
2207 1831
2208 - // Page info
2209 - $page_url = $originating_columns_exist && !empty($session_data->originating_page_url) ? $session_data->originating_page_url : '';
2210 - $page_title = '';
2211 - if ($page_url) {
2212 - $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : parse_url($page_url, PHP_URL_PATH);
2213 - }
1832 + if ($start_page > 1) {
1833 + echo '<button class="mxchat-pagination-button" data-page="1">1</button>';
1834 + if ($start_page > 2) {
1835 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1836 + }
1837 + }
2214 1838
2215 - // Format messages for output
2216 - $formatted_messages = [];
2217 - foreach ($messages as $msg) {
2218 - $is_user = ($msg->role === 'user');
2219 - $is_bot = ($msg->role === 'bot' || $msg->role === 'assistant');
1839 + for ($i = $start_page; $i <= $end_page; $i++) {
1840 + $class = $i === $page ? 'mxchat-pagination-button active' : 'mxchat-pagination-button';
1841 + echo '<button class="' . $class . '" data-page="' . $i . '">' . $i . '</button>';
1842 + }
2220 1843
2221 - $content = wp_kses(
2222 - stripslashes($msg->message),
2223 - [
2224 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2225 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2226 - 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []],
2227 - 'div' => ['class' => [], 'id' => [], 'data-nonce' => []],
2228 - 'img' => ['src' => [], 'alt' => [], 'class' => []],
2229 - 'h3' => ['class' => []],
2230 - 'h4' => ['class' => []],
2231 - 'button' => ['type' => [], 'class' => [], 'data-product-id' => [], 'data-nonce' => [], 'data-product-type' => [], 'data-original-text' => [], 'data-mxchat-action' => []],
2232 - 'select' => ['class' => [], 'data-attribute' => []],
2233 - 'option' => ['value' => []],
2234 - 'span' => ['class' => []],
2235 - 'del' => [], 'ins' => [],
2236 - ]
2237 - );
2238 - $formatted_content = $this->format_transcript_message($content);
1844 + if ($end_page < $total_pages) {
1845 + if ($end_page < $total_pages - 1) {
1846 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1847 + }
1848 + echo '<button class="mxchat-pagination-button" data-page="' . $total_pages . '">' . $total_pages . '</button>';
1849 + }
2239 1850
2240 - $has_rag = $is_bot && !empty($msg->rag_context);
1851 + if ($page < $total_pages) {
1852 + echo '<button class="mxchat-pagination-button" data-page="' . ($page + 1) . '">' . esc_html__('Next', 'mxchat') . ' &raquo;</button>';
1853 + }
2241 1854
2242 - $formatted_messages[] = [
2243 - 'id' => $msg->id,
2244 - 'role' => $msg->role,
2245 - 'is_user' => $is_user,
2246 - 'is_bot' => $is_bot,
2247 - 'content' => $formatted_content,
2248 - 'timestamp' => wp_date('g:i A', strtotime($msg->timestamp . ' UTC')),
2249 - 'full_timestamp' => wp_date('F j, Y g:i A', strtotime($msg->timestamp . ' UTC')),
2250 - 'has_rag' => $has_rag
2251 - ];
1855 + echo '</div>';
2252 1856 }
2253 1857
2254 - // First message timestamp for "started" display
2255 - $started = !empty($messages) ? wp_date('M j, Y g:i A', strtotime($messages[0]->timestamp . ' UTC')) : '-';
1858 + echo '</div>';
2256 1859
2257 1860 wp_send_json([
2258 - 'success' => true,
2259 - 'session_id' => $session_id,
2260 - 'user' => [
2261 - 'name' => $display_name,
2262 - 'sub' => $display_sub,
2263 - 'initials' => $initials,
2264 - 'email' => $user_email,
2265 - 'identifier' => $user_identifier
2266 - ],
2267 - 'page' => [
2268 - 'url' => $page_url,
2269 - 'title' => $page_title
2270 - ],
2271 - 'clicked_urls' => $clicked_urls,
2272 - 'messages' => $formatted_messages,
2273 - 'message_count' => count($messages),
2274 - 'started' => $started
1861 + 'html' => ob_get_clean(),
1862 + 'page' => $page,
1863 + 'total_pages' => $total_pages,
1864 + 'total_sessions' => $total_sessions
2275 1865 ]);
1866 +
2276 1867 wp_die();
2277 1868 }
2278 1869
2279 1870
@@ -2369,9 +1960,9 @@
2369 1960 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
2370 1961
2371 1962 // Display success message if all prompts were deleted
2372 1963 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
2373 - echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'mxchat') . '</span></button></div>';
1964 + echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
2374 1965 }
2375 1966
2376 1967 // Set up pagination, search query, and content type filter
2377 1968 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
@@ -2377,9 +1968,9 @@
2377 1968 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
2378 1969 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
2379 1970 $content_type_filter = isset($_GET['content_type']) ? sanitize_key($_GET['content_type']) : ''; // ADDED 2.5.6
2380 1971 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
2381 - $per_page = 25;
1972 + $per_page = 10;
2382 1973
2383 1974 //error_log('DEBUG: Search query: ' . $search_query);
2384 1975 //error_log('DEBUG: Content type filter: ' . $content_type_filter);
2385 1976 //error_log('DEBUG: Current page: ' . $current_page);
@@ -2417,12 +2008,8 @@
2417 2008 $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($current_bot_id);
2418 2009 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
2419 2010 $pinecone_api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
2420 2011
2421 - // Get OpenAI Vector Store settings
2422 - $vectorstore_options = get_option('mxchat_openai_vectorstore_options', array());
2423 - $use_vectorstore = ($vectorstore_options['mxchat_use_openai_vectorstore'] ?? '0') === '1';
2424 -
2425 2012 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
2426 2013 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Has API Key: ' . (!empty($pinecone_api_key) ? 'YES' : 'NO'));
2427 2014 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
2428 2015 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Namespace: ' . ($pinecone_options['mxchat_pinecone_namespace'] ?? 'NOT SET'));
@@ -2488,111 +2075,25 @@
2488 2075 if (!empty($where_clauses)) {
2489 2076 $sql_where = "WHERE " . implode(" AND ", $where_clauses);
2490 2077 }
2491 2078
2492 - // UPDATED 2.6.3: Count unique entries (by source_url) instead of individual rows
2493 - // This ensures pagination shows X entries per page, not X chunks
2494 - // Entries with empty source_url are counted individually
2079 + // Retrieve total number of prompts, considering filters
2495 2080 if (!empty($where_values)) {
2496 - // Count unique source_urls + count of rows with empty source_url
2497 - $count_query = $wpdb->prepare(
2498 - "SELECT (SELECT COUNT(DISTINCT source_url) FROM {$table_name} {$sql_where} AND source_url != '') +
2499 - (SELECT COUNT(*) FROM {$table_name} {$sql_where} AND (source_url = '' OR source_url IS NULL))",
2500 - array_merge($where_values, $where_values)
2501 - );
2081 + $count_query = $wpdb->prepare("SELECT COUNT(*) FROM {$table_name} {$sql_where}", $where_values);
2502 2082 } else {
2503 - $count_query = "SELECT (SELECT COUNT(DISTINCT source_url) FROM {$table_name} WHERE source_url != '') +
2504 - (SELECT COUNT(*) FROM {$table_name} WHERE source_url = '' OR source_url IS NULL)";
2083 + $count_query = "SELECT COUNT(*) FROM {$table_name} {$sql_where}";
2505 2084 }
2506 2085 $total_records = $wpdb->get_var($count_query);
2507 2086 $total_pages = ceil($total_records / $per_page);
2508 2087
2509 - // UPDATED 2.6.3: Get unique source_urls for pagination, then fetch all their rows
2510 - // Step 1: Get the source_urls for this page (distinct URLs ordered by latest timestamp)
2511 - if (!empty($where_values)) {
2512 - $urls_query = $wpdb->prepare(
2513 - "SELECT source_url, MAX(timestamp) as latest_ts FROM {$table_name} {$sql_where}
2514 - GROUP BY source_url ORDER BY latest_ts DESC LIMIT %d OFFSET %d",
2515 - array_merge($where_values, array($per_page, $offset))
2516 - );
2517 - } else {
2518 - $urls_query = $wpdb->prepare(
2519 - "SELECT source_url, MAX(timestamp) as latest_ts FROM {$table_name}
2520 - GROUP BY source_url ORDER BY latest_ts DESC LIMIT %d OFFSET %d",
2521 - $per_page, $offset
2522 - );
2523 - }
2524 - $page_urls = $wpdb->get_results($urls_query);
2088 + // Retrieve prompts from the database
2089 + $limit_offset_values = array_merge($where_values, array($per_page, $offset));
2090 + $prompts_query = $wpdb->prepare(
2091 + "SELECT * FROM {$table_name} {$sql_where} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
2092 + ...$limit_offset_values
2093 + );
2525 2094
2526 - // Step 2: Fetch all rows for these source_urls
2527 - $prompts = array();
2528 - if (!empty($page_urls)) {
2529 - // Build URL order map to preserve newest-first ordering from step 1
2530 - $url_order_map = array();
2531 - $url_list = array();
2532 - $has_empty_url = false;
2533 - $order_index = 0;
2534 - foreach ($page_urls as $url_row) {
2535 - if (empty($url_row->source_url)) {
2536 - $has_empty_url = true;
2537 - $url_order_map['__empty__'] = $order_index++;
2538 - } else {
2539 - $url_list[] = $url_row->source_url;
2540 - $url_order_map[$url_row->source_url] = $order_index++;
2541 - }
2542 - }
2543 -
2544 - // Build query to fetch all rows for these URLs
2545 - $url_conditions = array();
2546 - $url_values = array();
2547 -
2548 - if (!empty($url_list)) {
2549 - $placeholders = implode(',', array_fill(0, count($url_list), '%s'));
2550 - $url_conditions[] = "source_url IN ($placeholders)";
2551 - $url_values = array_merge($url_values, $url_list);
2552 - }
2553 -
2554 - if ($has_empty_url) {
2555 - $url_conditions[] = "(source_url = '' OR source_url IS NULL)";
2556 - }
2557 -
2558 - if (!empty($url_conditions)) {
2559 - $url_where = "WHERE (" . implode(" OR ", $url_conditions) . ")";
2560 -
2561 - // Add original filters back
2562 - if (!empty($where_clauses)) {
2563 - $url_where .= " AND " . implode(" AND ", $where_clauses);
2564 - $url_values = array_merge($url_values, $where_values);
2565 - }
2566 -
2567 - if (!empty($url_values)) {
2568 - $prompts_query = $wpdb->prepare(
2569 - "SELECT * FROM {$table_name} {$url_where} ORDER BY timestamp DESC",
2570 - $url_values
2571 - );
2572 - } else {
2573 - $prompts_query = "SELECT * FROM {$table_name} {$url_where} ORDER BY timestamp DESC";
2574 - }
2575 -
2576 - $prompts = $wpdb->get_results($prompts_query);
2577 -
2578 - // Sort prompts by the original URL order (newest first), then by timestamp within each URL group
2579 - usort($prompts, function($a, $b) use ($url_order_map) {
2580 - $url_a = empty($a->source_url) ? '__empty__' : $a->source_url;
2581 - $url_b = empty($b->source_url) ? '__empty__' : $b->source_url;
2582 - $order_a = $url_order_map[$url_a] ?? PHP_INT_MAX;
2583 - $order_b = $url_order_map[$url_b] ?? PHP_INT_MAX;
2584 -
2585 - // First sort by URL order (newest URLs first)
2586 - if ($order_a !== $order_b) {
2587 - return $order_a - $order_b;
2588 - }
2589 -
2590 - // Within same URL, sort by timestamp DESC (newest chunks first)
2591 - return strtotime($b->timestamp) - strtotime($a->timestamp);
2592 - });
2593 - }
2594 - }
2095 + $prompts = $wpdb->get_results($prompts_query);
2595 2096 }
2596 2097
2597 2098 // ================================
2598 2099 // PAGINATION GENERATION
@@ -2599,35 +2100,29 @@
2599 2100 // ================================
2600 2101
2601 2102 // Generate pagination links
2602 2103 if ($total_pages > 1) {
2603 - // Build a clean base URL with only necessary parameters
2604 - $pagination_args = array('page' => 'mxchat-prompts');
2605 -
2104 + // Build base URL with necessary parameters
2105 + $base_url = add_query_arg('paged', '%#%');
2106 +
2606 2107 // Preserve bot_id parameter if multi-bot is active
2607 2108 if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') {
2608 - $pagination_args['bot_id'] = $current_bot_id;
2109 + $base_url = add_query_arg('bot_id', $current_bot_id, $base_url);
2609 2110 }
2610 2111
2611 - // Preserve search query and nonce if present
2112 + // Preserve search query if present
2612 2113 if ($search_query) {
2613 - $pagination_args['search'] = $search_query;
2614 - if (!empty($nonce)) {
2615 - $pagination_args['_wpnonce'] = $nonce;
2616 - }
2114 + $base_url = add_query_arg('search', $search_query, $base_url);
2617 2115 }
2618 2116
2619 - // Preserve content type filter if present
2117 + // UPDATED 2.5.6: Preserve content type filter if present
2620 2118 if ($content_type_filter) {
2621 - $pagination_args['content_type'] = $content_type_filter;
2119 + $base_url = add_query_arg('content_type', $content_type_filter, $base_url);
2622 2120 }
2623 -
2624 - // Build clean base URL
2625 - $base_url = add_query_arg($pagination_args, admin_url('admin.php'));
2626 -
2121 +
2627 2122 $page_links = paginate_links(array(
2628 - 'base' => $base_url . '%_%',
2629 - 'format' => '&paged=%#%',
2123 + 'base' => $base_url,
2124 + 'format' => '',
2630 2125 'prev_text' => __('&laquo; Previous', 'mxchat'),
2631 2126 'next_text' => __('Next &raquo;', 'mxchat'),
2632 2127 'total' => $total_pages,
2633 2128 'current' => $current_page,
@@ -2639,50 +2134,1198 @@
2639 2134
2640 2135 // ================================
2641 2136 // PROCESSING STATUS RETRIEVAL
2642 2137 // ================================
2138 +
2139 + // Retrieve processing statuses
2140 + $pdf_url = get_transient('mxchat_last_pdf_url');
2141 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
2142 + $pdf_status = $pdf_url ? $knowledge_manager->mxchat_get_pdf_processing_status($pdf_url) : false;
2143 + $sitemap_status = $sitemap_url ? $knowledge_manager->mxchat_get_sitemap_processing_status($sitemap_url) : false;
2643 2144
2644 - // Retrieve processing statuses using queue-based method
2645 - $processing_statuses = $knowledge_manager->mxchat_get_processing_statuses();
2646 - $pdf_status = $processing_statuses['pdf_status'];
2647 - $sitemap_status = $processing_statuses['sitemap_status'];
2648 - $is_processing = $processing_statuses['is_processing'];
2145 + $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
2146 + || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
2649 2147
2650 2148 //error_log('=== DEBUG: mxchat_create_prompts_page data preparation completed ===');
2651 2149
2652 - // ================================
2653 - // RENDER PAGE WITH NEW SIDEBAR LAYOUT
2654 - // ================================
2150 + ?>
2655 2151
2656 - // Include the new knowledge page template
2657 - require_once plugin_dir_path(__FILE__) . 'admin-knowledge-page.php';
2152 + <div class="wrap mxchat-wrapper">
2153 + <!-- Hero Section -->
2154 + <div class="mxchat-hero">
2155 + <h1 class="mxchat-main-title">
2156 + <span class="mxchat-gradient-text">Knowledge Base</span> Manager
2157 + </h1>
2158 + <p class="mxchat-hero-subtitle">
2159 + <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
2160 + </p>
2161 + </div>
2658 2162
2659 - // Package all page data for the render function
2660 - $page_data = array(
2661 - 'prompts' => $prompts,
2662 - 'total_records' => $total_records,
2663 - 'total_pages' => $total_pages,
2664 - 'current_page' => $current_page,
2665 - 'per_page' => $per_page,
2666 - 'page_links' => $page_links,
2667 - 'search_query' => $search_query,
2668 - 'content_type_filter' => $content_type_filter,
2669 - 'data_source' => $data_source,
2670 - 'use_pinecone' => $use_pinecone,
2671 - 'use_vectorstore' => $use_vectorstore,
2672 - 'multibot_active' => $multibot_active,
2673 - 'current_bot_id' => $current_bot_id,
2674 - 'pdf_status' => $pdf_status,
2675 - 'sitemap_status' => $sitemap_status,
2676 - 'is_processing' => $is_processing,
2677 - 'total_in_database' => $total_in_database ?? 0,
2678 - 'showing_recent_only' => $showing_recent_only ?? false,
2679 - );
2163 + <div class="mxchat-content">
2164 +
2165 + <!-- Multi-Bot Selector (only shows if multi-bot addon is active) -->
2166 + <?php if (class_exists('MxChat_Multi_Bot_Manager')) :
2167 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
2168 + $available_bots = $multi_bot_manager->get_available_bots();
2169 +
2170 + // Get saved bot selection (user-specific first, then site-wide default)
2171 + $user_id = get_current_user_id();
2172 + $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
2173 + if (empty($saved_bot_id)) {
2174 + $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
2175 + }
2176 +
2177 + // Allow URL override but default to saved selection
2178 + $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
2179 + ?>
2180 + <div class="mxchat-card" style="margin-bottom: 20px;">
2181 + <div class="mxchat-bot-selector-wrapper" style="display: flex; align-items: center; gap: 15px;">
2182 + <label for="mxchat-bot-selector" style="font-weight: 600;">
2183 + <?php esc_html_e('Select Bot Database:', 'mxchat'); ?>
2184 + </label>
2185 + <select id="mxchat-bot-selector" class="mxchat-bot-selector" style="min-width: 200px;">
2186 + <?php foreach ($available_bots as $bot_id => $bot_name) : ?>
2187 + <option value="<?php echo esc_attr($bot_id); ?>" <?php selected($current_bot_id, $bot_id); ?>>
2188 + <?php echo esc_html($bot_name); ?>
2189 + </option>
2190 + <?php endforeach; ?>
2191 + </select>
2192 + <span class="mxchat-bot-info" style="color: #666; font-size: 13px;">
2193 + <?php esc_html_e('Content will be added to the selected bot\'s knowledge base', 'mxchat'); ?>
2194 + </span>
2195 + <span id="mxchat-bot-save-status" style="display: none; color: #00a32a; font-size: 13px;">
2196 + ✓ <?php esc_html_e('Saved', 'mxchat'); ?>
2197 + </span>
2198 + </div>
2199 + </div>
2680 2200
2681 - // Render the new sidebar-based page
2682 - mxchat_render_knowledge_page($this, $knowledge_manager, $page_data);
2201 + <?php
2202 + // Set a flag so we know multi-bot is active throughout the page
2203 + $multibot_active = true;
2204 + else:
2205 + $current_bot_id = 'default';
2206 + $multibot_active = false;
2207 + endif;
2208 + ?>
2209 +
2210 + <!-- Tab Navigation -->
2211 + <div class="mxchat-kb-tabs-nav">
2212 + <button class="mxchat-kb-tab-button active" data-tab="import">
2213 + <?php esc_html_e('Knowledge Import', 'mxchat'); ?>
2214 + </button>
2215 + <button class="mxchat-kb-tab-button" data-tab="sync">
2216 + <?php esc_html_e('Settings', 'mxchat'); ?>
2217 + </button>
2218 + <button class="mxchat-kb-tab-button" data-tab="pinecone">
2219 + <?php esc_html_e('Pinecone Settings', 'mxchat'); ?>
2220 + </button>
2221 + </div>
2222 +
2223 + <div class="mxchat-kb-tabs-content">
2224 + <div id="mxchat-kb-tab-import" class="mxchat-kb-tab-content active">
2225 + <!-- Import Options Card -->
2226 + <div class="mxchat-card">
2227 +
2228 + <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
2229 +
2230 + <?php
2231 + // Check if the appropriate embedding API key exists
2232 + $embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
2233 + $has_openai_key = !empty($this->options['api_key']);
2234 + $has_voyage_key = !empty($this->options['voyage_api_key']);
2235 + $has_gemini_key = !empty($this->options['gemini_api_key']);
2236 +
2237 + // Determine if they have the needed API key for their selected embedding model
2238 + $has_required_key = false;
2239 + $required_key_type = '';
2240 +
2241 + if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
2242 + $has_required_key = true;
2243 + $required_key_type = 'OpenAI';
2244 + } elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
2245 + $has_required_key = true;
2246 + $required_key_type = 'Voyage AI';
2247 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false && $has_gemini_key) {
2248 + $has_required_key = true;
2249 + $required_key_type = 'Google Gemini';
2250 + } elseif (strpos($embedding_model, 'text-embedding-') !== false) {
2251 + $required_key_type = 'OpenAI';
2252 + } elseif (strpos($embedding_model, 'voyage-') !== false) {
2253 + $required_key_type = 'Voyage AI';
2254 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false) {
2255 + $required_key_type = 'Google Gemini';
2256 + }
2257 + ?>
2258 +
2259 + <div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
2260 + <?php if ($has_required_key): ?>
2261 + <p><span class="dashicons dashicons-yes-alt"></span> <?php echo wp_kses_post(sprintf(__('We detected your %s API key. <strong>Remember to add credits to your %s account</strong> before using the knowledgebase.', 'mxchat'), $required_key_type, $required_key_type)); ?></p>
2262 + <?php else: ?>
2263 + <p><span class="dashicons dashicons-warning"></span> <strong><?php esc_html_e('Important:', 'mxchat'); ?></strong> <?php echo sprintf(esc_html__('Before importing knowledge, you must add a %s API key with sufficient credits in the Chatbot settings.', 'mxchat'), $required_key_type); ?> <a href="<?php echo admin_url('admin.php?page=mxchat-max'); ?>"><?php esc_html_e('Go to API Key Settings', 'mxchat'); ?></a></p>
2264 + <?php endif; ?>
2265 + </div>
2266 +
2267 + <!-- Import Options Section -->
2268 + <div class="mxchat-import-section">
2269 + <h3><?php esc_html_e('Import Options', 'mxchat'); ?></h3>
2270 +
2271 + <!-- Import Options Grid -->
2272 + <div class="mxchat-import-options">
2273 + <!-- WordPress Import Option -->
2274 + <button type="button" id="mxchat-open-content-selector" class="mxchat-import-box mxchat-import-wordpress" data-option="wordpress">
2275 + <div class="mxchat-import-icon">
2276 + <span class="dashicons dashicons-wordpress"></span>
2277 + </div>
2278 + <div class="mxchat-import-content">
2279 + <h4><?php esc_html_e('WordPress Content', 'mxchat'); ?></h4>
2280 + <p><?php esc_html_e('Import specific posts and pages to your knowledge base.', 'mxchat'); ?></p>
2281 + </div>
2282 + <div class="mxchat-recommended-tag"><?php esc_html_e('Recommended', 'mxchat'); ?></div>
2283 + </button>
2284 +
2285 + <!-- Sitemap Import Option -->
2286 + <button type="button" class="mxchat-import-box" data-option="sitemap" data-placeholder="<?php esc_attr_e('Enter sitemap URL here', 'mxchat'); ?>" data-type="sitemap">
2287 + <div class="mxchat-import-icon">
2288 + <span class="dashicons dashicons-admin-site-alt"></span>
2289 + </div>
2290 + <div class="mxchat-import-content">
2291 + <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
2292 + <p><?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
2293 + </div>
2294 + </button>
2295 +
2296 + <!-- Direct URL Import Option -->
2297 + <button type="button" class="mxchat-import-box" data-option="url" data-placeholder="<?php esc_attr_e('Enter webpage URL here', 'mxchat'); ?>" data-type="url">
2298 + <div class="mxchat-import-icon">
2299 + <span class="dashicons dashicons-admin-links"></span>
2300 + </div>
2301 + <div class="mxchat-import-content">
2302 + <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
2303 + <p><?php esc_html_e('Import content from any webpage.', 'mxchat'); ?></p>
2304 + </div>
2305 + </button>
2306 +
2307 + <!-- Direct Content Import Option -->
2308 + <button type="button" class="mxchat-import-box" data-option="content" data-type="content">
2309 + <div class="mxchat-import-icon">
2310 + <span class="dashicons dashicons-editor-paste-text"></span>
2311 + </div>
2312 + <div class="mxchat-import-content">
2313 + <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
2314 + <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p>
2315 + </div>
2316 + </button>
2317 +
2318 + <!-- PDF Import Option -->
2319 + <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf">
2320 + <div class="mxchat-import-icon">
2321 + <span class="dashicons dashicons-media-document"></span>
2322 + </div>
2323 + <div class="mxchat-import-content">
2324 + <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
2325 + <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p>
2326 + </div>
2327 + </button>
2328 + </div>
2329 +
2330 + <!-- Input Areas for URL and Content -->
2331 + <div class="mxchat-import-input-area" id="mxchat-url-input-area" style="display: none;">
2332 + <?php if (!$is_processing) : ?>
2333 + <form id="mxchat-url-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
2334 + <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
2335 + <input type="hidden" name="import_type" id="import_type" value="url">
2336 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
2337 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
2338 + <?php endif; ?>
2339 + <div class="mxchat-url-input-group">
2340 + <input type="url"
2341 + name="sitemap_url"
2342 + id="sitemap_url"
2343 + placeholder="<?php esc_attr_e('Enter URL here', 'mxchat'); ?>"
2344 + required />
2345 + <button type="submit"
2346 + name="submit_sitemap"
2347 + class="mxchat-button-primary">
2348 + <?php esc_html_e('Import', 'mxchat'); ?>
2349 + </button>
2350 + </div>
2351 + <p class="mxchat-url-description" id="url-description-text"></p>
2352 + </form>
2353 + <?php endif; ?>
2354 + </div>
2355 +
2356 + <div class="mxchat-import-input-area" id="mxchat-content-input-area" style="display: none;">
2357 + <?php if (!$is_processing) : ?>
2358 + <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
2359 + <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
2360 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
2361 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
2362 + <?php endif; ?>
2363 + <div class="mxchat-form-group">
2364 + <textarea
2365 + name="article_content"
2366 + id="article_content"
2367 + placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"
2368 + required
2369 + rows="6"
2370 + ></textarea>
2371 + </div>
2372 + <div class="mxchat-form-group">
2373 + <input type="url"
2374 + name="article_url"
2375 + id="article_url"
2376 + placeholder="<?php esc_attr_e('Enter source URL (Optional)', 'mxchat'); ?>">
2377 + <div class="mxchat-url-helper" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
2378 + <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: #666; cursor: pointer;">
2379 + <input type="checkbox" id="mxchat-unique-url-toggle" style="margin: 0;">
2380 + <?php esc_html_e('Generate unique URL for duplicate content', 'mxchat'); ?>
2381 + </label>
2382 + <button type="button"
2383 + id="mxchat-generate-unique-url"
2384 + class="mxchat-button-secondary"
2385 + style="padding: 4px 12px; font-size: 12px; display: none;">
2386 + <span class="dashicons dashicons-randomize" style="font-size: 14px; margin-top: 2px;"></span>
2387 + <?php esc_html_e('Generate Unique', 'mxchat'); ?>
2388 + </button>
2389 + </div>
2390 + <p class="description" style="margin-top: 8px; font-size: 12px; color: #666;">
2391 + <?php esc_html_e('Enable this option to submit multiple entries with the same base URL. A unique reference will be appended (e.g., ?ref=1731234567890).', 'mxchat'); ?>
2392 + </p>
2393 + </div>
2394 + <button type="submit"
2395 + name="submit_content"
2396 + class="mxchat-button-primary">
2397 + <?php esc_html_e('Import Content', 'mxchat'); ?>
2398 + </button>
2399 + </form>
2400 + <?php endif; ?>
2401 + </div>
2402 + </div>
2403 +
2404 + <!-- PDF Processing Status - ONLY PROCESSING -->
2405 + <?php if ($pdf_status && $pdf_status['status'] === 'processing') : ?>
2406 + <div class="mxchat-status-card" data-card-type="pdf">
2407 + <div class="mxchat-status-header">
2408 + <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
2409 +
2410 + <!-- Processing controls -->
2411 + <div class="mxchat-processing-controls">
2412 + <form method="post" class="mxchat-stop-form"
2413 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2414 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2415 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2416 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2417 + </button>
2418 + </form>
2419 +
2420 + <button type="button" class="mxchat-manual-batch-btn"
2421 + data-process-type="pdf"
2422 + data-url="<?php echo esc_attr(get_transient('mxchat_last_pdf_url')); ?>">
2423 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
2424 + </button>
2425 + </div>
2426 + </div>
2427 +
2428 + <div class="mxchat-progress-bar">
2429 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
2430 + </div>
2431 +
2432 + <div class="mxchat-status-details">
2433 + <p><?php printf(
2434 + esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
2435 + absint($pdf_status['processed_pages']),
2436 + absint($pdf_status['total_pages']),
2437 + absint($pdf_status['percentage'])
2438 + ); ?></p>
2439 +
2440 + <?php if (!empty($pdf_status['failed_pages']) && $pdf_status['failed_pages'] > 0) : ?>
2441 + <p><strong><?php esc_html_e('Failed pages:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['failed_pages']); ?></p>
2442 + <?php endif; ?>
2443 +
2444 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($pdf_status['status'])); ?></p>
2445 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['last_update']); ?></p>
2446 + </div>
2447 + </div>
2448 + <?php endif; ?>
2449 +
2450 + <!-- Sitemap Processing Status - ONLY PROCESSING -->
2451 + <?php if ($sitemap_status && $sitemap_status['status'] === 'processing') : ?>
2452 + <div class="mxchat-status-card" data-card-type="sitemap">
2453 + <div class="mxchat-status-header">
2454 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
2455 +
2456 + <!-- Processing controls -->
2457 + <div class="mxchat-processing-controls">
2458 + <form method="post" class="mxchat-stop-form"
2459 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2460 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2461 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2462 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2463 + </button>
2464 + </form>
2465 +
2466 + <button type="button" class="mxchat-manual-batch-btn"
2467 + data-process-type="sitemap"
2468 + data-url="<?php echo esc_attr(get_transient('mxchat_last_sitemap_url')); ?>">
2469 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
2470 + </button>
2471 + </div>
2472 + </div>
2473 +
2474 + <div class="mxchat-progress-bar">
2475 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
2476 + </div>
2477 +
2478 + <div class="mxchat-status-details">
2479 + <p><?php printf(
2480 + esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
2481 + absint($sitemap_status['processed_urls']),
2482 + absint($sitemap_status['total_urls']),
2483 + absint($sitemap_status['percentage'])
2484 + ); ?></p>
2485 +
2486 + <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
2487 + <p><strong><?php esc_html_e('Failed URLs:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['failed_urls']); ?></p>
2488 + <?php endif; ?>
2489 +
2490 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($sitemap_status['status'])); ?></p>
2491 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['last_update']); ?></p>
2492 +
2493 + <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
2494 + <div class="mxchat-error-notice">
2495 + <?php if (!empty($sitemap_status['error'])) : ?>
2496 + <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
2497 + <?php endif; ?>
2498 + <?php if (!empty($sitemap_status['last_error'])) : ?>
2499 + <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
2500 + <?php endif; ?>
2501 + </div>
2502 + <?php endif; ?>
2503 + </div>
2504 + </div>
2505 + <?php endif; ?>
2506 +
2507 + <!-- Single URL Submission Status -->
2508 + <?php
2509 + // Get single URL status
2510 + $single_url_status = $this->knowledge_manager->mxchat_get_single_url_status();
2511 + $is_active_processing =
2512 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
2513 + ($pdf_status && $pdf_status['status'] === 'processing');
2514 + ?>
2515 +
2516 + <div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
2517 + <?php if ($single_url_status && !$is_active_processing) : ?>
2518 + <div class="mxchat-status-card">
2519 + <div class="mxchat-status-header">
2520 + <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
2521 + <?php if ($single_url_status['status'] === 'failed') : ?>
2522 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
2523 + <?php else : ?>
2524 + <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
2525 + <?php endif; ?>
2526 + </div>
2527 + <div class="mxchat-status-details">
2528 + <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
2529 + <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
2530 + <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
2531 + </a>
2532 + </p>
2533 + <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
2534 +
2535 + <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
2536 + <div class="mxchat-error-notice">
2537 + <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
2538 + </div>
2539 + <?php endif; ?>
2540 +
2541 + <?php if ($single_url_status['status'] === 'complete') : ?>
2542 + <p><strong><?php esc_html_e('Content Length:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['content_length']); ?> <?php esc_html_e('characters', 'mxchat'); ?></p>
2543 + <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
2544 + <?php endif; ?>
2545 + </div>
2546 + </div>
2547 + <?php endif; ?>
2548 + </div>
2549 +
2550 + <!-- Completed Status Cards - Handles completed PDF/Sitemap processing -->
2551 + <?php
2552 + echo $knowledge_manager->mxchat_render_completed_status_cards();
2553 + ?>
2554 +
2555 + </div>
2556 +
2557 + <!-- Knowledge Base Table Card -->
2558 + <div class="mxchat-card">
2559 + <div class="mxchat-card-header">
2560 + <h2>
2561 + <?php esc_html_e('Knowledge Base', 'mxchat'); ?>
2562 + <span class="mxchat-record-count">
2563 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2564 + (<?php echo esc_html($total_records); ?> of <?php echo esc_html(number_format($total_in_database)); ?> total - recent entries only)
2565 + <?php else: ?>
2566 + (<?php echo esc_html($total_records); ?>)
2567 + <?php endif; ?>
2568 + </span>
2569 + <!-- Data source badge -->
2570 + <?php if ($use_pinecone) : ?>
2571 + <span class="mxchat-data-source-badge pinecone" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #e3f2fd; color: #1976d2;">
2572 + <span class="dashicons dashicons-cloud"></span>
2573 + <?php esc_html_e('Pinecone', 'mxchat'); ?>
2574 + </span>
2575 + <?php else : ?>
2576 + <span class="mxchat-data-source-badge wordpress" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #f3e5f5; color: #7b1fa2;">
2577 + <span class="dashicons dashicons-database-view"></span>
2578 + <?php esc_html_e('WordPress DB', 'mxchat'); ?>
2579 + </span>
2580 + <?php endif; ?>
2581 + </h2>
2582 + <div class="mxchat-header-actions">
2583 + <!-- Search -->
2584 + <form method="get" id="knowledge-search" class="mxchat-search-form">
2585 + <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
2586 + <input type="hidden" name="page" value="mxchat-prompts" />
2587 + <?php if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') : ?>
2588 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2589 + <?php endif; ?>
2590 + <div class="mxchat-search-group">
2591 + <span class="dashicons dashicons-search"></span>
2592 + <input type="text"
2593 + name="search"
2594 + placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
2595 + value="<?php echo esc_attr($search_query); ?>" />
2596 + </div>
2597 +
2598 + <!-- ADDED 2.5.6: Content Type Filter -->
2599 + <div class="mxchat-filter-group">
2600 + <select name="content_type" id="mxchat-content-type-filter" onchange="this.form.submit()">
2601 + <option value=""><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2602 + <option value="post" <?php selected($content_type_filter, 'post'); ?>><?php esc_html_e('Posts', 'mxchat'); ?></option>
2603 + <option value="page" <?php selected($content_type_filter, 'page'); ?>><?php esc_html_e('Pages', 'mxchat'); ?></option>
2604 + <option value="product" <?php selected($content_type_filter, 'product'); ?>><?php esc_html_e('Products', 'mxchat'); ?></option>
2605 + <option value="pdf" <?php selected($content_type_filter, 'pdf'); ?>><?php esc_html_e('PDFs', 'mxchat'); ?></option>
2606 + <option value="url" <?php selected($content_type_filter, 'url'); ?>><?php esc_html_e('URLs', 'mxchat'); ?></option>
2607 + <option value="content" <?php selected($content_type_filter, 'content'); ?>><?php esc_html_e('Manual Content', 'mxchat'); ?></option>
2608 + </select>
2609 + </div>
2610 + </form>
2611 +
2612 + <form method="post"
2613 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
2614 + class="mxchat-delete-form"
2615 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
2616 + <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
2617 + <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" />
2618 + <!-- Always include bot_id, even if it's 'default' -->
2619 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2620 + <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
2621 + <span class="dashicons dashicons-trash"></span>
2622 + <?php esc_html_e('Delete All', 'mxchat'); ?>
2623 + </button>
2624 + </form>
2625 + </div>
2626 + </div>
2627 +
2628 + <!-- Recent Entries Info Banner -->
2629 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2630 + <div class="mxchat-info-banner recent-only" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e3f2fd; border-left: 4px solid #2196f3; color: #1565c0;">
2631 + <span class="dashicons dashicons-info"></span>
2632 + <span>
2633 + <?php printf(
2634 + esc_html__('Showing your %s most recent entries for faster loading. Your complete database contains %s entries. ', 'mxchat'),
2635 + number_format($total_records),
2636 + number_format($total_in_database)
2637 + ); ?>
2638 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('View complete database in Pinecone →', 'mxchat'); ?></a>
2639 + </span>
2640 + </div>
2641 + <?php endif; ?>
2642 +
2643 + <!-- Data Source Info Banner -->
2644 + <?php if ($use_pinecone) : ?>
2645 + <div class="mxchat-info-banner pinecone" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e8f5e8; border-left: 4px solid #4caf50; color: #2e7d2e;">
2646 + <span class="dashicons dashicons-cloud"></span>
2647 + <span><?php esc_html_e('Refresh page to see new content. New entires or deletions can take up to 60 seconds to reflect! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2648 + </div>
2649 + <?php else : ?>
2650 + <div class="mxchat-info-banner wordpress" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #fff3e0; border-left: 4px solid #ff9800; color: #e65100;">
2651 + <span class="dashicons dashicons-database-view"></span>
2652 + <span><?php esc_html_e('Refresh page to see new content, but wait 5-10 seconds first! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2653 + </div>
2654 + <?php endif; ?>
2655 +
2656 + <!-- Table -->
2657 + <div class="mxchat-table-wrapper">
2658 + <table class="mxchat-records-table">
2659 + <thead>
2660 + <tr>
2661 + <th><?php esc_html_e('ID', 'mxchat'); ?></th>
2662 + <th><?php esc_html_e('Content', 'mxchat'); ?></th>
2663 + <th><?php esc_html_e('Source', 'mxchat'); ?></th>
2664 + <?php if ($data_source === 'pinecone') : ?>
2665 + <th><?php esc_html_e('Vector ID', 'mxchat'); ?></th>
2666 + <?php endif; ?>
2667 + <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2668 + </tr>
2669 + </thead>
2670 + <tbody>
2671 + <?php if ($prompts) : ?>
2672 + <?php foreach ($prompts as $index => $prompt) : ?>
2673 + <tr id="prompt-<?php echo esc_attr($prompt->id); ?>"
2674 + data-source="<?php echo esc_attr($data_source); ?>"
2675 + <?php if ($data_source === 'pinecone') : ?>
2676 + style="background: rgba(33, 150, 243, 0.02);"
2677 + <?php endif; ?>>
2678 + <td>
2679 + <?php if ($data_source === 'pinecone') : ?>
2680 + <?php echo esc_html($index + 1 + (($current_page - 1) * $per_page)); ?>
2681 + <?php else : ?>
2682 + <?php echo esc_html($prompt->id); ?>
2683 + <?php endif; ?>
2684 + </td>
2685 + <td class="mxchat-content-cell">
2686 + <div class="mxchat-accordion-wrapper">
2687 + <div class="mxchat-content-preview">
2688 + <?php
2689 + $content = $prompt->article_content;
2690 + $preview_length = 150;
2691 + $content_preview = mb_strlen($content) > $preview_length
2692 + ? mb_substr($content, 0, $preview_length) . '...'
2693 + : $content;
2694 + ?>
2695 + <span class="preview-text"><?php echo esc_html($content_preview); ?></span>
2696 + <?php if (mb_strlen($content) > $preview_length) : ?>
2697 + <button class="mxchat-expand-toggle" type="button">
2698 + <span class="dashicons dashicons-arrow-down-alt2"></span>
2699 + </button>
2700 + <?php endif; ?>
2701 + </div>
2702 + <div class="mxchat-content-full" style="display: none;">
2703 + <div class="content-view">
2704 + <?php
2705 + // Check if content contains Hebrew characters
2706 + if (preg_match('/[\x{0590}-\x{05FF}]/u', $content)) {
2707 + // Apply RTL direction for Hebrew content
2708 + echo '<div dir="rtl" lang="he" class="rtl-content">';
2709 + echo wp_kses_post(wpautop($content));
2710 + echo '</div>';
2711 + } else {
2712 + echo wp_kses_post(wpautop($content));
2713 + }
2714 + ?>
2715 + </div>
2716 + <?php if ($data_source === 'wordpress') : ?>
2717 + <textarea class="content-edit" style="display:none;"
2718 + <?php if (preg_match('/[\x{0590}-\x{05FF}]/u', $prompt->article_content)) echo 'dir="rtl" lang="he"'; ?>>
2719 + <?php echo htmlspecialchars($prompt->article_content, ENT_QUOTES, 'UTF-8'); ?>
2720 + </textarea>
2721 + <?php endif; ?>
2722 + </div>
2723 + </div>
2724 + </td>
2725 + <td class="mxchat-url-cell">
2726 + <div class="url-view">
2727 + <?php if (!empty($prompt->source_url)) : ?>
2728 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
2729 + <span class="dashicons dashicons-external"></span>
2730 + <?php esc_html_e('View Source', 'mxchat'); ?>
2731 + </a>
2732 + <?php else : ?>
2733 + <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
2734 + <?php endif; ?>
2735 + </div>
2736 + <?php if ($data_source === 'wordpress') : ?>
2737 + <input type="text" class="url-edit" style="display:none;"
2738 + value="<?php echo htmlspecialchars($prompt->source_url, ENT_QUOTES, 'UTF-8'); ?>" />
2739 + <?php endif; ?>
2740 + </td>
2741 + <?php if ($data_source === 'pinecone') : ?>
2742 + <td class="mxchat-vector-id-cell">
2743 + <code style="background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 11px;">
2744 + <?php echo esc_html(substr($prompt->id, 0, 12) . '...'); ?>
2745 + </code>
2746 + </td>
2747 + <?php endif; ?>
2748 +
2749 + <td class="mxchat-actions-cell">
2750 + <?php if ($data_source === 'wordpress') : ?>
2751 + <!-- WordPress - Full editing capabilities -->
2752 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2753 + <select class="mxchat-role-select"
2754 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2755 + data-data-source="wordpress"
2756 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2757 + <?php
2758 + $current_role = $prompt->role_restriction ?? 'public';
2759 + $role_options = $knowledge_manager->mxchat_get_role_options();
2760 + foreach ($role_options as $role_key => $role_label) : ?>
2761 + <option value="<?php echo esc_attr($role_key); ?>"
2762 + <?php selected($current_role, $role_key); ?>>
2763 + <?php echo esc_html($role_label); ?>
2764 + </option>
2765 + <?php endforeach; ?>
2766 + </select>
2767 + </div>
2768 +
2769 + <!-- Edit/Save Buttons -->
2770 + <div class="mxchat-action-buttons">
2771 + <button class="mxchat-button-icon edit-button"
2772 + data-id="<?php echo esc_attr($prompt->id); ?>">
2773 + <span class="dashicons dashicons-edit"></span>
2774 + </button>
2775 + <button class="mxchat-button-icon save-button"
2776 + data-id="<?php echo esc_attr($prompt->id); ?>"
2777 + style="display:none;"
2778 + data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
2779 + <span class="dashicons dashicons-saved"></span>
2780 + </button>
2781 + </div>
2782 + <?php else : ?>
2783 + <!-- Pinecone - Role restriction editable, content read-only -->
2784 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2785 + <select class="mxchat-role-select"
2786 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2787 + data-data-source="pinecone"
2788 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2789 + <?php
2790 + $current_role = $prompt->role_restriction ?? 'public';
2791 + $role_options = $knowledge_manager->mxchat_get_role_options();
2792 + foreach ($role_options as $role_key => $role_label) : ?>
2793 + <option value="<?php echo esc_attr($role_key); ?>"
2794 + <?php selected($current_role, $role_key); ?>>
2795 + <?php echo esc_html($role_label); ?>
2796 + </option>
2797 + <?php endforeach; ?>
2798 + </select>
2799 + </div>
2800 +
2801 + <?php endif; ?>
2802 +
2803 + <!-- Delete button for both sources -->
2804 + <div class="mxchat-delete-container">
2805 + <?php if ($data_source === 'pinecone') : ?>
2806 + <!-- AJAX Delete for Pinecone -->
2807 + <button type="button"
2808 + class="mxchat-button-icon delete-button-ajax"
2809 + data-vector-id="<?php echo esc_attr($prompt->id); ?>"
2810 + data-bot-id="<?php echo esc_attr($current_bot_id); ?>"
2811 + data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
2812 + title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
2813 + <span class="dashicons dashicons-trash"></span>
2814 + </button>
2815 + <?php else : ?>
2816 + <!-- Regular link delete for WordPress DB -->
2817 + <a href="<?php echo esc_url(admin_url(
2818 + 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
2819 + . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
2820 + )); ?>"
2821 + class="mxchat-button-icon delete-button"
2822 + onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
2823 + <span class="dashicons dashicons-trash"></span>
2824 + </a>
2825 + <?php endif; ?>
2826 + </div>
2827 + </td>
2828 +
2829 + </tr>
2830 + <?php endforeach; ?>
2831 + <?php else : ?>
2832 + <tr>
2833 + <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
2834 + <?php if ($use_pinecone) : ?>
2835 + <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
2836 + <?php else : ?>
2837 + <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
2838 + <?php endif; ?>
2839 + </td>
2840 + </tr>
2841 + <?php endif; ?>
2842 + </tbody>
2843 + </table>
2844 + </div>
2845 +
2846 + <?php if ($page_links) : ?>
2847 + <div class="mxchat-pagination">
2848 + <?php echo wp_kses_post($page_links); ?>
2849 + </div>
2850 + <?php endif; ?>
2851 + </div>
2852 +
2853 + </div>
2854 +
2855 + <!-- Settings Tab (Renamed from Auto-Sync Settings) -->
2856 + <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
2857 +
2858 + <!-- Auto-Sync Settings Card -->
2859 + <div class="mxchat-card">
2860 + <div class="mxchat-settings-section">
2861 + <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
2862 + <p class="mxchat-description">
2863 + <?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'); ?>
2864 + </p>
2865 + <div class="mxchat-autosave-section">
2866 + <div class="mxchat-toggle-group">
2867 + <div class="mxchat-toggle-container">
2868 + <label class="mxchat-toggle-switch">
2869 + <input type="checkbox"
2870 + name="mxchat_auto_sync_posts"
2871 + class="mxchat-autosave-field"
2872 + value="1"
2873 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2874 + <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
2875 + <span class="mxchat-toggle-slider"></span>
2876 + </label>
2877 + <span class="mxchat-toggle-label">
2878 + <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
2879 + </span>
2880 + </div>
2881 +
2882 + <div class="mxchat-toggle-container">
2883 + <label class="mxchat-toggle-switch">
2884 + <input type="checkbox"
2885 + name="mxchat_auto_sync_pages"
2886 + class="mxchat-autosave-field"
2887 + value="1"
2888 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2889 + <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
2890 + <span class="mxchat-toggle-slider"></span>
2891 + </label>
2892 + <span class="mxchat-toggle-label">
2893 + <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
2894 + </span>
2895 + </div>
2896 +
2897 + <!-- Custom Post Types Section -->
2898 + <div class="mxchat-section-content">
2899 + <div class="mxchat-custom-post-types-header">
2900 + <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
2901 + <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
2902 + <span class="mxchat-toggle-icon">▼</span>
2903 + </button>
2904 + </div>
2905 +
2906 + <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
2907 + <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
2908 + <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
2909 +
2910 + <div class="mxchat-custom-post-types">
2911 + <?php
2912 + $post_types = $knowledge_manager->mxchat_get_public_post_types();
2913 + // Skip post and page as they're handled separately
2914 + unset($post_types['post']);
2915 + unset($post_types['page']);
2916 +
2917 + if (!empty($post_types)) {
2918 + foreach ($post_types as $post_type => $label) {
2919 + $option_name = 'mxchat_auto_sync_' . $post_type;
2920 + $is_enabled = get_option($option_name, '0');
2921 + ?>
2922 + <div class="mxchat-toggle-container">
2923 + <label class="mxchat-toggle-switch">
2924 + <input type="checkbox"
2925 + name="<?php echo esc_attr($option_name); ?>"
2926 + class="mxchat-autosave-field"
2927 + value="1"
2928 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2929 + <?php checked($is_enabled, '1'); ?>>
2930 + <span class="mxchat-toggle-slider"></span>
2931 + </label>
2932 + <span class="mxchat-toggle-label">
2933 + <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
2934 + </span>
2935 + </div>
2936 + <?php
2937 + }
2938 + } else {
2939 + echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
2940 + }
2941 + ?>
2942 + </div>
2943 + </div>
2944 + </div>
2945 + </div>
2946 + </div>
2947 + </div>
2948 + </div>
2949 +
2950 + <!-- Role-Based Content Restrictions Card -->
2951 + <div class="mxchat-card" style="margin-top: 20px;">
2952 + <h3><?php esc_html_e('Role-Based Content Restrictions', 'mxchat'); ?></h3>
2953 + <p class="mxchat-description">
2954 + <?php esc_html_e('Automatically restrict content access based on WordPress tags. When you assign a tag to a role, any post with that tag will only be accessible to users with that role or higher in the chatbot knowledge base.', 'mxchat'); ?>
2955 + </p>
2956 +
2957 + <!-- Info Box -->
2958 + <div class="mxchat-knowledge-warning info" style="background: #e3f2fd; border-left-color: #2196f3;">
2959 + <p>
2960 + <span class="dashicons dashicons-info"></span>
2961 + <strong><?php esc_html_e('How it works:', 'mxchat'); ?></strong>
2962 + <?php esc_html_e('Add a tag below and select which role should have access. Any content tagged with that tag will automatically be restricted to that role level. Works with both auto-sync and manual imports.', 'mxchat'); ?>
2963 + </p>
2964 + </div>
2965 +
2966 + <!-- Add New Tag-Role Mapping -->
2967 + <div class="mxchat-role-mapping-form">
2968 + <h4><?php esc_html_e('Add Tag-Role Mapping', 'mxchat'); ?></h4>
2969 +
2970 + <div class="mxchat-form-row" style="display: flex; gap: 15px; align-items: flex-end; margin-bottom: 20px;">
2971 + <div class="mxchat-form-group" style="flex: 1;">
2972 + <label for="mxchat-tag-input">
2973 + <?php esc_html_e('Tag Name', 'mxchat'); ?>
2974 + </label>
2975 + <input type="text"
2976 + id="mxchat-tag-input"
2977 + class="regular-text"
2978 + placeholder="<?php esc_attr_e('Enter tag name (e.g., premium, members-only)', 'mxchat'); ?>" />
2979 + <p class="description">
2980 + <?php esc_html_e('Enter the exact tag slug as it appears in WordPress', 'mxchat'); ?>
2981 + </p>
2982 + </div>
2983 +
2984 + <div class="mxchat-form-group" style="flex: 1;">
2985 + <label for="mxchat-role-select">
2986 + <?php esc_html_e('Required Role', 'mxchat'); ?>
2987 + </label>
2988 + <select id="mxchat-role-select" class="regular-text">
2989 + <?php
2990 + // Get role options from knowledge manager
2991 + $role_options = array(
2992 + 'public' => __('Public (Everyone)', 'mxchat'),
2993 + 'logged_in' => __('Logged In Users', 'mxchat'),
2994 + 'subscriber' => __('Subscribers & Above', 'mxchat'),
2995 + 'contributor' => __('Contributors & Above', 'mxchat'),
2996 + 'author' => __('Authors & Above', 'mxchat'),
2997 + 'editor' => __('Editors & Above', 'mxchat'),
2998 + 'administrator' => __('Administrators Only', 'mxchat')
2999 + );
3000 +
3001 + foreach ($role_options as $role_key => $role_label) : ?>
3002 + <option value="<?php echo esc_attr($role_key); ?>">
3003 + <?php echo esc_html($role_label); ?>
3004 + </option>
3005 + <?php endforeach; ?>
3006 + </select>
3007 + <p class="description">
3008 + <?php esc_html_e('Select the minimum role required to access this content', 'mxchat'); ?>
3009 + </p>
3010 + </div>
3011 +
3012 + <div style="padding-bottom: 25px;">
3013 + <button type="button"
3014 + id="mxchat-add-tag-role"
3015 + class="mxchat-button-primary">
3016 + <span class="dashicons dashicons-plus-alt"></span>
3017 + <?php esc_html_e('Add Mapping', 'mxchat'); ?>
3018 + </button>
3019 + </div>
3020 + </div>
3021 + </div>
3022 +
3023 + <!-- Existing Tag-Role Mappings -->
3024 + <div class="mxchat-role-mappings-list">
3025 + <h4><?php esc_html_e('Current Tag-Role Mappings', 'mxchat'); ?></h4>
3026 +
3027 + <div id="mxchat-mappings-container">
3028 + <!-- Mappings will be loaded here via AJAX -->
3029 + <div class="mxchat-loading-mappings">
3030 + <span class="mxchat-spinner is-active"></span>
3031 + <?php esc_html_e('Loading mappings...', 'mxchat'); ?>
3032 + </div>
3033 + </div>
3034 +
3035 + <!-- Empty State (initially hidden) -->
3036 + <div id="mxchat-no-mappings" style="display: none; text-align: center; padding: 40px; color: #666;">
3037 + <span class="dashicons dashicons-tag" style="font-size: 48px; opacity: 0.3;"></span>
3038 + <p><?php esc_html_e('No tag-role mappings yet. Add your first mapping above to get started.', 'mxchat'); ?></p>
3039 + </div>
3040 + </div>
3041 +
3042 + <!-- Bulk Update Section -->
3043 + <div class="mxchat-bulk-update-section" style="margin-top: 30px; padding-top: 30px; border-top: 1px solid #ddd;">
3044 + <h4><?php esc_html_e('Bulk Update Existing Content', 'mxchat'); ?></h4>
3045 + <p class="mxchat-description">
3046 + <?php esc_html_e('Apply role restrictions to all existing content that has the mapped tags. This is useful when setting up tag-role mappings for the first time or after changing mappings.', 'mxchat'); ?>
3047 + </p>
3048 +
3049 + <div class="mxchat-bulk-update-controls">
3050 + <button type="button"
3051 + id="mxchat-bulk-update-roles"
3052 + class="mxchat-button-secondary">
3053 + <span class="dashicons dashicons-update"></span>
3054 + <?php esc_html_e('Update All Existing Content', 'mxchat'); ?>
3055 + </button>
3056 +
3057 + <div id="mxchat-bulk-update-progress" style="display: none; margin-top: 15px;">
3058 + <div class="mxchat-progress-bar">
3059 + <div class="mxchat-progress-fill" style="width: 0%"></div>
3060 + </div>
3061 + <p class="mxchat-progress-text" style="margin-top: 10px; color: #666;">
3062 + <?php esc_html_e('Processing...', 'mxchat'); ?>
3063 + </p>
3064 + </div>
3065 +
3066 + <div id="mxchat-bulk-update-result" style="display: none; margin-top: 15px;"></div>
3067 + </div>
3068 + </div>
3069 + </div>
3070 +
3071 + </div>
3072 +
3073 + <!-- Pinecone Settings Tab -->
3074 + <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
3075 + <div class="mxchat-card">
3076 + <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
3077 + <p class="mxchat-description">
3078 + <?php echo wp_kses(
3079 + __('<strong>Pinecone is optional</strong> and not required for MxChat to function. It provides enhanced search performance for larger knowledge bases. When enabled, content will be stored in Pinecone instead of the WordPress database, but you can use MxChat without it.', 'mxchat'),
3080 + array('strong' => array())
3081 + ); ?>
3082 + </p>
3083 +
3084 + <div class="mxchat-pinecone-info-box">
3085 + <div class="mxchat-info-icon">
3086 + <span class="dashicons dashicons-info"></span>
3087 + </div>
3088 + <div class="mxchat-info-content">
3089 + <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
3090 + <p><?php esc_html_e('Pinecone is a specialized vector database designed for AI applications. It provides faster similarity searches and better scalability compared to traditional databases for AI-powered features.', 'mxchat'); ?></p>
3091 + <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
3092 + </div>
3093 + </div>
3094 +
3095 + <div class="mxchat-database-settings-form">
3096 + <?php
3097 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
3098 + $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
3099 + ?>
3100 +
3101 + <div class="mxchat-toggle-container">
3102 + <label class="mxchat-toggle-switch">
3103 + <input type="checkbox"
3104 + name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
3105 + value="1"
3106 + <?php checked($use_pinecone, '1'); ?>>
3107 + <span class="mxchat-toggle-slider"></span>
3108 + </label>
3109 + <span class="mxchat-toggle-label">
3110 + <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
3111 + </span>
3112 + </div>
3113 +
3114 + <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
3115 +
3116 + <?php if ($use_pinecone) : ?>
3117 + <div class="mxchat-knowledge-warning success">
3118 + <p><span class="dashicons dashicons-yes-alt"></span>
3119 + <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
3120 + </p>
3121 + </div>
3122 + <?php endif; ?>
3123 +
3124 + <div class="mxchat-form-group">
3125 + <label for="mxchat_pinecone_api_key">
3126 + <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
3127 + </label>
3128 + <input type="password"
3129 + id="mxchat_pinecone_api_key"
3130 + name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
3131 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
3132 + class="regular-text"
3133 + placeholder="pcsk_..." />
3134 + <p class="description">
3135 + <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
3136 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
3137 + </p>
3138 + </div>
3139 +
3140 + <div class="mxchat-form-group">
3141 + <label for="mxchat_pinecone_environment">
3142 + <?php esc_html_e('Region', 'mxchat'); ?>
3143 + </label>
3144 + <input type="text"
3145 + id="mxchat_pinecone_environment"
3146 + name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
3147 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
3148 + placeholder="e.g., gcp-starter"
3149 + class="regular-text" />
3150 + <p class="description">
3151 + <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
3152 + </p>
3153 + </div>
3154 +
3155 + <div class="mxchat-form-group">
3156 + <label for="mxchat_pinecone_index">
3157 + <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
3158 + </label>
3159 + <input type="text"
3160 + id="mxchat_pinecone_index"
3161 + name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
3162 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
3163 + placeholder="e.g., my-wordpress-vectors"
3164 + class="regular-text" />
3165 + <p class="description">
3166 + <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
3167 + </p>
3168 + </div>
3169 +
3170 + <div class="mxchat-form-group">
3171 + <label for="mxchat_pinecone_host">
3172 + <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
3173 + </label>
3174 + <input type="text"
3175 + id="mxchat_pinecone_host"
3176 + name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
3177 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
3178 + placeholder="e.g., my-index-xyz123.svc.pinecone.io"
3179 + class="regular-text" />
3180 + <p class="description">
3181 + <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
3182 + </p>
3183 + </div>
3184 +
3185 + <div class="mxchat-setup-steps">
3186 + <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
3187 + <div class="mxchat-setup-step">
3188 + <span class="step-number">1</span>
3189 + <div class="step-content">
3190 + <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
3191 + <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
3192 + </div>
3193 + </div>
3194 + <div class="mxchat-setup-step">
3195 + <span class="step-number">2</span>
3196 + <div class="step-content">
3197 + <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
3198 + <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
3199 + <ul>
3200 + <li><?php esc_html_e('Dimensions: Choose based on your embedding model - 1536 (OpenAI Ada 2, TE3 Small), 2048 (Voyage-3 Large), 3072 (TE3 Large, Gemini Embedding), 1536 (Gemini Embedding), or 768 (Gemini Embedding)', 'mxchat'); ?></li>
3201 + <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
3202 + <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
3203 + </ul>
3204 + </div>
3205 + </div>
3206 + <div class="mxchat-setup-step">
3207 + <span class="step-number">3</span>
3208 + <div class="step-content">
3209 + <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
3210 + <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
3211 + </div>
3212 + </div>
3213 + <div class="mxchat-setup-step">
3214 + <span class="step-number">4</span>
3215 + <div class="step-content">
3216 + <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
3217 + <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
3218 + </div>
3219 + </div>
3220 + <div class="mxchat-setup-step">
3221 + <span class="step-number">5</span>
3222 + <div class="step-content">
3223 + <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
3224 + <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
3225 + </div>
3226 + </div>
3227 + </div>
3228 +
3229 + </div>
3230 +
3231 + </div>
3232 + </div>
3233 + </div>
3234 +
3235 + </div>
3236 +
3237 + </div>
3238 + </div>
3239 +
3240 + <!-- Content Selector Modal -->
3241 + <div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
3242 + <div class="mxchat-kb-modal-content">
3243 + <div class="mxchat-kb-modal-header">
3244 + <h3>
3245 + <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
3246 + <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
3247 + </h3>
3248 + <span class="mxchat-kb-modal-close">&times;</span>
3249 + </div>
3250 + <div class="mxchat-kb-modal-filters">
3251 + <div class="mxchat-kb-search-group">
3252 + <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
3253 + </div>
3254 +
3255 + <div class="mxchat-kb-filter-group">
3256 + <select id="mxchat-kb-content-type-filter">
3257 + <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
3258 + <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
3259 + <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
3260 + <?php
3261 + // Add other post types dynamically
3262 + $post_types = get_post_types(array('public' => true), 'objects');
3263 + foreach ($post_types as $post_type) {
3264 + // Skip post and page as they're already added
3265 + if (!in_array($post_type->name, array('post', 'page'))) {
3266 + echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
3267 + }
3268 + }
3269 + ?>
3270 + </select>
3271 +
3272 + <select id="mxchat-kb-content-status-filter">
3273 + <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
3274 + <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
3275 + <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
3276 + </select>
3277 +
3278 + <select id="mxchat-kb-processed-filter">
3279 + <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
3280 + <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
3281 + <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
3282 + </select>
3283 + </div>
3284 + </div>
3285 +
3286 + <div class="mxchat-kb-content-selection">
3287 + <div class="mxchat-kb-selection-header">
3288 + <label>
3289 + <input type="checkbox" id="mxchat-kb-select-all">
3290 + <?php esc_html_e('Select All', 'mxchat'); ?>
3291 + </label>
3292 + <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
3293 + </div>
3294 +
3295 + <div class="mxchat-kb-content-list">
3296 + <!-- Content will be loaded here via AJAX -->
3297 + <div class="mxchat-kb-loading">
3298 + <span class="mxchat-kb-spinner is-active"></span>
3299 + <?php esc_html_e('Loading content...', 'mxchat'); ?>
3300 + </div>
3301 + </div>
3302 +
3303 + <div class="mxchat-kb-pagination">
3304 + <!-- Pagination will be added here -->
3305 + </div>
3306 + </div>
3307 +
3308 + <div class="mxchat-kb-modal-footer">
3309 + <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
3310 + <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
3311 + <span class="mxchat-kb-selected-count">(0)</span>
3312 + </button>
3313 + <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
3314 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3315 + </button>
3316 + </div>
3317 + </div>
3318 + </div>
3319 +
3320 +<?php
2683 3321 }
2684 3322
3323 +
3324 +
3325 +
3326 +
3327 +
2685 3328 /**
2686 3329 * Get bot-specific Pinecone configuration
2687 3330 * Used in the knowledge retrieval functions
2688 3331 */
@@ -2730,9 +3373,8 @@
2730 3373 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2731 3374
2732 3375 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
2733 3376 $deleted_count = 0;
2734 - $translations_table = $wpdb->prefix . 'mxchat_transcript_translations';
2735 3377
2736 3378 foreach ($_POST['delete_session_ids'] as $session_id) {
2737 3379 $session_id_sanitized = sanitize_text_field($session_id);
2738 3380
@@ -2742,13 +3384,8 @@
2742 3384
2743 3385 // Perform the deletion from the database table
2744 3386 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
2745 3387
2746 - // Delete any saved translations for this session
2747 - if ($wpdb->get_var("SHOW TABLES LIKE '$translations_table'") === $translations_table) {
2748 - $wpdb->delete($translations_table, ['session_id' => $session_id_sanitized]);
2749 - }
2750 -
2751 3388 // Delete the corresponding option entry from wp_options table
2752 3389 delete_option("mxchat_history_" . $session_id_sanitized);
2753 3390
2754 3391 // Delete any associated metadata options
@@ -2772,157 +3409,8 @@
2772 3409 }
2773 3410
2774 3411 wp_die();
2775 3412 }
2776 -
2777 -/**
2778 - * Format transcript message content with markdown processing
2779 - * Converts markdown links, bold, italic, code blocks, and plain URLs to HTML
2780 - */
2781 -private function format_transcript_message($text) {
2782 - if (empty($text)) {
2783 - return '';
2784 - }
2785 -
2786 - // Normalize line endings and clean up excessive whitespace
2787 - $text = str_replace("\r\n", "\n", $text);
2788 - $text = str_replace("\r", "\n", $text);
2789 -
2790 - // Clean up existing <br> tags that may have been saved (legacy data)
2791 - // Convert <br>, <br/>, <br /> back to newlines for consistent processing
2792 - $text = preg_replace('/<br\s*\/?>\s*/i', "\n", $text);
2793 -
2794 - // Collapse 3+ consecutive newlines to just 2 (paragraph break)
2795 - $text = preg_replace('/\n{3,}/', "\n\n", $text);
2796 -
2797 - // Process markdown headers (# Header)
2798 - $text = preg_replace_callback('/^(#{1,6})\s+(.+)$/m', function($matches) {
2799 - $level = strlen($matches[1]);
2800 - $content = esc_html(trim($matches[2]));
2801 - return "<h{$level}>{$content}</h{$level}>";
2802 - }, $text);
2803 -
2804 - // Process code blocks with triple backticks
2805 - $text = preg_replace_callback('/```(\w+)?\n?([\s\S]*?)```/', function($matches) {
2806 - $language = !empty($matches[1]) ? ' class="language-' . esc_attr($matches[1]) . '"' : '';
2807 - $code = esc_html($matches[2]);
2808 - return "<pre><code{$language}>{$code}</code></pre>";
2809 - }, $text);
2810 -
2811 - // Process inline code with single backticks
2812 - $text = preg_replace('/`([^`]+)`/', '<code>$1</code>', $text);
2813 -
2814 - // Process bold text **text** or __text__
2815 - $text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
2816 - $text = preg_replace('/__(.+?)__/', '<strong>$1</strong>', $text);
2817 -
2818 - // Process italic text *text* or _text_ (but not if part of URL)
2819 - $text = preg_replace('/(?<![*_\w])\*([^*]+)\*(?![*\w])/', '<em>$1</em>', $text);
2820 - $text = preg_replace('/(?<![*_\w])_([^_]+)_(?![*\w])/', '<em>$1</em>', $text);
2821 -
2822 - // Process markdown links [text](url)
2823 - $text = preg_replace_callback('/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/', function($matches) {
2824 - $link_text = esc_html($matches[1]);
2825 - $url = esc_url($matches[2]);
2826 - return "<a href=\"{$url}\" target=\"_blank\" rel=\"noopener\">{$link_text}</a>";
2827 - }, $text);
2828 -
2829 - // Process citation-style brackets [URL]
2830 - $text = preg_replace_callback('/\[(https?:\/\/[^\]]+)\]/', function($matches) {
2831 - $url = esc_url($matches[1]);
2832 - return "<a href=\"{$url}\" target=\"_blank\" rel=\"noopener\">{$url}</a>";
2833 - }, $text);
2834 -
2835 - // Process standalone URLs (not already in links or img src)
2836 - $text = preg_replace_callback(
2837 - '/(?<!href="|src="|">)(https?:\/\/[^\s<>"]+)(?![^<]*<\/a>)/',
2838 - function($matches) {
2839 - $url = esc_url($matches[1]);
2840 - // Truncate display URL if too long
2841 - $display = strlen($matches[1]) > 50 ? substr($matches[1], 0, 47) . '...' : $matches[1];
2842 - return "<a href=\"{$url}\" target=\"_blank\" rel=\"noopener\">{$display}</a>";
2843 - },
2844 - $text
2845 - );
2846 -
2847 - // Process mailto links
2848 - $text = preg_replace_callback('/\[([^\]]+)\]\((mailto:[^)]+)\)/', function($matches) {
2849 - $link_text = esc_html($matches[1]);
2850 - $mailto = esc_url($matches[2]);
2851 - return "<a href=\"{$mailto}\">{$link_text}</a>";
2852 - }, $text);
2853 -
2854 - // Convert paragraphs: split by double newlines, wrap in <p> tags
2855 - // This creates proper paragraph structure instead of excessive <br> tags
2856 - $paragraphs = preg_split('/\n\n+/', $text);
2857 -
2858 - // Filter out empty paragraphs but preserve content like "0"
2859 - $paragraphs = array_values(array_filter(array_map('trim', $paragraphs), function($p) {
2860 - return $p !== '';
2861 - }));
2862 -
2863 - if (empty($paragraphs)) {
2864 - // No content after filtering
2865 - return '';
2866 - } elseif (count($paragraphs) > 1) {
2867 - // Multiple paragraphs - wrap each in <p> tags, convert single newlines to <br>
2868 - $formatted_paragraphs = array_map(function($p) {
2869 - return nl2br($p);
2870 - }, $paragraphs);
2871 - $text = '<p>' . implode('</p><p>', $formatted_paragraphs) . '</p>';
2872 - } else {
2873 - // Single paragraph - just convert newlines to <br>
2874 - $text = nl2br($paragraphs[0]);
2875 - }
2876 -
2877 - return $text;
2878 -}
2879 -
2880 -/**
2881 - * AJAX handler to fetch RAG context for a specific message
2882 - * Used by the transcript viewer to show retrieved documents
2883 - */
2884 -public function mxchat_get_rag_context() {
2885 - // Check permissions
2886 - if (!current_user_can('manage_options')) {
2887 - wp_send_json_error(['message' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
2888 - wp_die();
2889 - }
2890 -
2891 - // Validate message ID
2892 - if (!isset($_POST['message_id']) || empty($_POST['message_id'])) {
2893 - wp_send_json_error(['message' => esc_html__('Message ID is required.', 'mxchat')]);
2894 - wp_die();
2895 - }
2896 -
2897 - $message_id = absint($_POST['message_id']);
2898 -
2899 - global $wpdb;
2900 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2901 -
2902 - // Fetch the RAG context for this message
2903 - $result = $wpdb->get_row($wpdb->prepare(
2904 - "SELECT rag_context FROM {$table_name} WHERE id = %d",
2905 - $message_id
2906 - ));
2907 -
2908 - if (!$result || empty($result->rag_context)) {
2909 - wp_send_json_error(['message' => esc_html__('No RAG context found for this message.', 'mxchat')]);
2910 - wp_die();
2911 - }
2912 -
2913 - // Decode the JSON data
2914 - $rag_context = json_decode($result->rag_context, true);
2915 -
2916 - if (json_last_error() !== JSON_ERROR_NONE) {
2917 - wp_send_json_error(['message' => esc_html__('Invalid RAG context data.', 'mxchat')]);
2918 - wp_die();
2919 - }
2920 -
2921 - wp_send_json_success($rag_context);
2922 - wp_die();
2923 -}
2924 -
2925 3413 public function display_admin_notices() {
2926 3414 // Check if we're on a MXChat admin page
2927 3415 $screen = get_current_screen();
2928 3416 if (!$screen || strpos($screen->base, 'mxchat') === false) {
@@ -2928,44 +3416,173 @@
2928 3416 if (!$screen || strpos($screen->base, 'mxchat') === false) {
2929 3417 return;
2930 3418 }
2931 3419
2932 - $dismiss_button = '<button type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'mxchat') . '</span></button>';
3420 + //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
2933 3421
2934 3422 // Check for error notices
2935 3423 $error_notice = get_transient('mxchat_admin_notice_error');
2936 3424 if ($error_notice) {
2937 - echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p>' . $dismiss_button . '</div>';
3425 + //error_log('Found error transient: ' . $error_notice);
3426 + echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
2938 3427 delete_transient('mxchat_admin_notice_error');
3428 + //error_log('Displayed and deleted error transient');
3429 + } else {
3430 + //error_log('No error transient found');
2939 3431 }
2940 3432
2941 3433 // Check for success notices
2942 3434 $success_notice = get_transient('mxchat_admin_notice_success');
2943 3435 if ($success_notice) {
2944 - echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p>' . $dismiss_button . '</div>';
3436 + //error_log('Found success transient: ' . $success_notice);
3437 + echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
2945 3438 delete_transient('mxchat_admin_notice_success');
3439 + //error_log('Displayed and deleted success transient');
2946 3440 }
2947 3441
2948 3442 // Check for info notices
2949 3443 $info_notice = get_transient('mxchat_admin_notice_info');
2950 3444 if ($info_notice) {
2951 - echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p>' . $dismiss_button . '</div>';
3445 + //error_log('Found info transient: ' . $info_notice);
3446 + echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
2952 3447 delete_transient('mxchat_admin_notice_info');
3448 + //error_log('Displayed and deleted info transient');
2953 3449 }
3450 +
2954 3451 }
2955 3452
2956 3453
2957 3454 public function mxchat_create_activation_page() {
2958 - // Include the new Pro & Extensions page template
2959 - require_once plugin_dir_path(__FILE__) . 'admin-pro-page.php';
3455 + $license_status = get_option('mxchat_license_status', 'inactive');
3456 + $license_error = get_option('mxchat_license_error', '');
3457 + $current_domain = parse_url(home_url(), PHP_URL_HOST);
2960 3458
2961 - // Get addons configuration from the MxChat_Addons class
2962 - require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php';
2963 - $addons_instance = new MxChat_Addons();
2964 - $addons_config = $addons_instance->get_addons_config();
3459 + $license_management_url = 'https://mxchat.ai/my-account/orders/';
2965 3460
2966 - // Render the consolidated Pro & Extensions page
2967 - mxchat_render_pro_page($this, $addons_config);
3461 + // Check if this activation has been linked to a domain
3462 + $is_domain_linked = $this->is_current_activation_linked($current_domain);
3463 + ?>
3464 + <div class="wrap mxchat-admin-activation">
3465 + <div class="mxchat-pro-hero">
3466 + <h1 class="pro-title">
3467 + <span class="pro-gradient-text">Manage</span> MxChat Pro
3468 + </h1>
3469 + <p class="pro-subtitle">
3470 + <?php if ($license_status === 'active'): ?>
3471 + <?php esc_html_e('Your pro license is active. Manage your activation below.', 'mxchat'); ?>
3472 + <?php else: ?>
3473 + <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
3474 + <?php endif; ?>
3475 + </p>
3476 + </div>
3477 +
3478 + <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
3479 + <div class="error notice">
3480 + <p><?php echo esc_html($license_error); ?></p>
3481 + </div>
3482 + <?php endif; ?>
3483 +
3484 + <!-- Domain Linking Notice for Existing Users -->
3485 + <?php if ($license_status === 'active' && !$is_domain_linked): ?>
3486 + <div class="notice notice-info">
3487 + <p>
3488 + <strong><?php esc_html_e('New Feature: Domain Tracking', 'mxchat'); ?></strong><br>
3489 + <?php esc_html_e('We\'ve enhanced our license system to track your activated domains. Link this activation to your domain for better license management.', 'mxchat'); ?>
3490 + </p>
3491 + <p>
3492 + <button type="button" id="link-domain-button" class="button button-secondary">
3493 + <?php esc_html_e('Link This Domain', 'mxchat'); ?>
3494 + </button>
3495 + <span id="domain-link-status" style="margin-left: 10px;"></span>
3496 + </p>
3497 + </div>
3498 + <?php endif; ?>
3499 +
3500 + <!-- Activation Form -->
3501 + <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
3502 + <div class="mxchat-pro-form-container">
3503 + <table class="form-table">
3504 + <tr valign="top">
3505 + <th scope="row"><?php esc_html_e('Email Address', 'mxchat'); ?></th>
3506 + <td>
3507 + <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text mxchat-pro-input" required />
3508 + </td>
3509 + </tr>
3510 + <tr valign="top">
3511 + <th scope="row"><?php esc_html_e('Activation Key', 'mxchat'); ?></th>
3512 + <td>
3513 + <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text mxchat-pro-input" required />
3514 + </td>
3515 + </tr>
3516 + </table>
3517 +
3518 + <!-- Hidden field for domain -->
3519 + <input type="hidden" id="mxchat_domain" name="domain" value="<?php echo esc_attr($current_domain); ?>" />
3520 +
3521 + <?php if ($license_status !== 'active'): ?>
3522 + <div class="mxchat-pro-button-container">
3523 + <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
3524 + <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
3525 + </div>
3526 + <?php endif; ?>
3527 + </div>
3528 + </form>
3529 +
3530 + <!-- License Status Display -->
3531 + <div class="mxchat-pro-status">
3532 + <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
3533 + <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
3534 + <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
3535 + </span>
3536 + </h3>
3537 +
3538 + <?php if ($license_status === 'active'): ?>
3539 + <div class="mxchat-license-details">
3540 + <p><strong><?php esc_html_e('Domain:', 'mxchat'); ?></strong> <?php echo esc_html($current_domain); ?>
3541 + <?php if ($is_domain_linked): ?>
3542 + <span style="color: green; margin-left: 10px;">✓ <?php esc_html_e('Linked', 'mxchat'); ?></span>
3543 + <?php else: ?>
3544 + <span style="color: orange; margin-left: 10px;">⚠ <?php esc_html_e('Not Linked', 'mxchat'); ?></span>
3545 + <?php endif; ?>
3546 + </p>
3547 + <p><strong><?php esc_html_e('Email:', 'mxchat'); ?></strong> <?php echo esc_html(get_option('mxchat_pro_email')); ?></p>
3548 + <p><strong><?php esc_html_e('License Key:', 'mxchat'); ?></strong>
3549 + <code style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px;">
3550 + <?php echo esc_html(get_option('mxchat_activation_key')); ?>
3551 + </code>
3552 + </p>
3553 + <p>
3554 + <small>
3555 + <?php esc_html_e('Manage all your licenses and domains on', 'mxchat'); ?>
3556 + <a href="<?php echo esc_url($license_management_url); ?>" target="_blank">
3557 + <?php esc_html_e('your account dashboard', 'mxchat'); ?>
3558 + </a>
3559 + </small>
3560 + </p>
3561 +
3562 + <!-- Deactivation Section -->
3563 + <div class="mxchat-deactivation-section" style="margin-top: 20px; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
3564 + <h4 style="margin-top: 0; color: #856404;">
3565 + <?php esc_html_e('License Management', 'mxchat'); ?>
3566 + </h4>
3567 + <p style="margin-bottom: 15px; color: #856404;">
3568 + <?php esc_html_e('Need to move this license to another site? Deactivate it here first, then activate it on your new site.', 'mxchat'); ?>
3569 + </p>
3570 + <button type="button" id="deactivate-license-button" class="button button-secondary" style="background: #dc3545; color: white; border-color: #dc3545;">
3571 + 🔓 <?php esc_html_e('Deactivate License', 'mxchat'); ?>
3572 + </button>
3573 + <span id="deactivate-status" style="margin-left: 10px;"></span>
3574 + </div>
3575 + </div>
3576 + <?php endif; ?>
3577 + </div>
3578 +
3579 + <!-- Hidden fields for JavaScript -->
3580 + <input type="hidden" id="mxchat-ajax-url" value="<?php echo admin_url('admin-ajax.php'); ?>" />
3581 + <input type="hidden" id="mxchat-nonce" value="<?php echo wp_create_nonce('mxchat_activate_license_nonce'); ?>" />
3582 + <input type="hidden" id="mxchat-api-url" value="https://mxchat.ai" />
3583 + </div>
3584 + <?php
2968 3585 }
2969 3586
2970 3587 /**
2971 3588 * Check if current activation is linked to a domain
@@ -2970,9 +3587,9 @@
2970 3587 /**
2971 3588 * Check if current activation is linked to a domain
2972 3589 * This checks YOUR website's database, not the user's local database
2973 3590 */
2974 -public function is_current_activation_linked($domain) {
3591 +private function is_current_activation_linked($domain) {
2975 3592 $license_key = get_option('mxchat_activation_key');
2976 3593 $email = get_option('mxchat_pro_email');
2977 3594
2978 3595 if (empty($license_key) || empty($email)) {
@@ -3003,54 +3620,50 @@
3003 3620 if (!current_user_can('manage_options')) {
3004 3621 return;
3005 3622 }
3006 3623
3624 + // Keep existing data fetching logic
3007 3625 global $wpdb;
3008 3626 $table_name = $wpdb->prefix . 'mxchat_intents';
3627 + $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
3628 + $per_page = 20;
3629 + $offset = ($page - 1) * $per_page;
3009 3630
3010 - // Get stats for dashboard
3011 - $total_actions = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
3012 - $enabled_actions = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE enabled = 1");
3013 - $disabled_actions = $total_actions - $enabled_actions;
3631 + // Success message
3632 + if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
3633 + echo '<div class="notice notice-success is-dismissible"><p>' .
3634 + esc_html__('Action updated successfully.', 'mxchat') .
3635 + '</p></div>';
3636 + }
3014 3637
3015 - // Get unique action types count
3016 - $action_types_count = $wpdb->get_var("SELECT COUNT(DISTINCT callback_function) FROM $table_name");
3638 + // Filtering logic
3639 + $where = '1=1';
3640 + $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
3641 + $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
3017 3642
3018 - // Get action type distribution
3019 - $type_distribution_raw = $wpdb->get_results("SELECT callback_function, COUNT(*) as count FROM $table_name GROUP BY callback_function ORDER BY count DESC LIMIT 10");
3020 - $available_callbacks = $this->mxchat_get_available_callbacks();
3021 - $callback_groups = $this->mxchat_get_available_callbacks(true, true);
3643 + if ($search_term) {
3644 + $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
3645 + $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
3646 + $search_term_like, $search_term_like);
3647 + }
3022 3648
3023 - $action_type_distribution = array();
3024 - foreach ($type_distribution_raw as $row) {
3025 - $label = isset($available_callbacks[$row->callback_function]['label'])
3026 - ? $available_callbacks[$row->callback_function]['label']
3027 - : $row->callback_function;
3028 - $action_type_distribution[$label] = $row->count;
3649 + if ($callback_filter) {
3650 + $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
3029 3651 }
3030 3652
3031 - // Prepare page data
3032 - $page_data = array(
3033 - 'total_actions' => $total_actions,
3034 - 'enabled_actions' => $enabled_actions,
3035 - 'disabled_actions' => $disabled_actions,
3036 - 'action_types_count' => $action_types_count,
3037 - 'action_type_distribution' => $action_type_distribution,
3038 - 'available_callbacks' => $available_callbacks,
3039 - 'callback_groups' => $callback_groups,
3040 - );
3653 + // Pagination
3654 + $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
3655 + $total_pages = ceil($total_intents / $per_page);
3041 3656
3042 - // Include and render the new template
3043 - require_once plugin_dir_path(__FILE__) . 'admin-actions-page.php';
3044 - mxchat_render_actions_page($this, $page_data);
3045 -}
3657 + // Get intents (now called actions)
3658 + $actions = $wpdb->get_results($wpdb->prepare(
3659 + "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
3660 + $per_page, $offset
3661 + ));
3046 3662
3047 -/**
3048 - * LEGACY HTML - Preserved below for reference, to be removed in future update
3049 - */
3050 -function mxchat_actions_page_legacy_html() {
3051 - // This function is deprecated and no longer used
3052 - // The new template is in includes/admin-actions-page.php
3663 + // Get callbacks
3664 + $available_callbacks = $this->mxchat_get_available_callbacks();
3665 +
3053 3666 ?>
3054 3667 <div class="wrap mxchat-wrapper">
3055 3668 <!-- Hero Section -->
3056 3669 <div class="mxchat-hero">
@@ -3326,14 +3939,14 @@
3326 3939 $category_slug = sanitize_title($group_label);
3327 3940
3328 3941 foreach ($group_callbacks as $function => $data) :
3329 3942 $label = $data['label'];
3943 + $pro_only = $data['pro_only'];
3330 3944 $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
3331 3945 $description = isset($data['description']) ? $data['description'] : '';
3332 3946 $is_addon = isset($data['addon']) && $data['addon'] !== false;
3333 3947 $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
3334 3948 $is_installed = isset($data['installed']) ? $data['installed'] : true;
3335 - $is_promo = !empty($data['addon_promo']) && !$is_installed;
3336 3949
3337 3950 // Determine card status and styling
3338 3951 $card_class = 'mxchat-action-type-card';
3339 3952 $icon_class = 'mxchat-action-type-icon';
@@ -3338,13 +3951,15 @@
3338 3951 $card_class = 'mxchat-action-type-card';
3339 3952 $icon_class = 'mxchat-action-type-icon';
3340 3953 $status_badge = '';
3341 3954
3342 - if ($is_promo) {
3343 - // Promotional card — not selectable, just informational
3344 - $card_class .= ' not-installed mxchat-promo-card';
3345 - $status_badge = '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3346 - } elseif ($is_addon && !$is_installed) {
3955 + if ($pro_only && !$this->is_activated) {
3956 + // Pro feature but no Pro license
3957 + $icon_class .= ' pro-feature';
3958 + $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
3959 + }
3960 +
3961 + if ($is_addon && !$is_installed) {
3347 3962 // Add-on not installed
3348 3963 $card_class .= ' not-installed';
3349 3964 $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3350 3965 }
@@ -3358,16 +3973,13 @@
3358 3973 }
3359 3974 ?>
3360 3975 <div class="<?php echo esc_attr($card_class); ?>"
3361 3976 data-category="<?php echo esc_attr($category_slug); ?>"
3362 - <?php if (!$is_promo) : ?>
3363 3977 data-value="<?php echo esc_attr($function); ?>"
3364 3978 data-label="<?php echo esc_attr($label); ?>"
3365 - <?php endif; ?>
3366 - data-pro="false"
3979 + data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
3367 3980 data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
3368 - data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>"
3369 - <?php if ($is_promo) : ?>data-promo="true"<?php endif; ?>>
3981 + data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
3370 3982 <div class="<?php echo esc_attr($icon_class); ?>">
3371 3983 <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
3372 3984 </div>
3373 3985 <div class="mxchat-action-type-info">
@@ -3376,15 +3988,14 @@
3376 3988 <?php if (!empty($status_badge)) : ?>
3377 3989 <?php echo $status_badge; ?>
3378 3990 <?php endif; ?>
3379 3991
3380 - <?php if ($is_promo || ($is_addon && !$is_installed)) : ?>
3992 + <?php if ($is_addon && !$is_installed) : ?>
3381 3993 <div class="mxchat-addon-info">
3382 3994 <?php echo esc_html(sprintf(
3383 3995 __('Requires %s', 'mxchat'),
3384 3996 $addon_name
3385 3997 )); ?>
3386 - — <a href="https://mxchat.ai/" target="_blank"><?php esc_html_e('Get Add-on', 'mxchat'); ?></a>
3387 3998 </div>
3388 3999 <?php endif; ?>
3389 4000 </div>
3390 4001 </div>
@@ -3711,15 +4322,12 @@
3711 4322 $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
3712 4323 return;
3713 4324 }
3714 4325
3715 - // Check if this is an add-on promotional placeholder (not a real action)
3716 - if (!empty($available_callbacks[$callback_function]['addon_promo'])) {
3717 - $addon_name = isset($available_callbacks[$callback_function]['addon_name']) ? $available_callbacks[$callback_function]['addon_name'] : __('an add-on', 'mxchat');
3718 - $this->handle_embedding_error(sprintf(
3719 - __('This action requires the %s to be installed and activated.', 'mxchat'),
3720 - $addon_name
3721 - ));
4326 + // Check Pro requirements
4327 + $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
4328 + if ($is_pro_only && !$this->is_activated) {
4329 + $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
3722 4330 return;
3723 4331 }
3724 4332
3725 4333 // Process phrases
@@ -3800,577 +4408,10 @@
3800 4408 exit;
3801 4409 }
3802 4410 }
3803 4411
3804 -/**
3805 - * AJAX handler to fetch actions list for the new split-panel UI
3806 - */
3807 -public function mxchat_fetch_actions_list() {
3808 - check_ajax_referer('mxchat_actions_nonce', 'security');
3809 4412
3810 - if (!current_user_can('manage_options')) {
3811 - wp_send_json_error(__('Unauthorized', 'mxchat'));
3812 - }
3813 -
3814 - global $wpdb;
3815 - $table_name = $wpdb->prefix . 'mxchat_intents';
3816 -
3817 - $page = isset($_POST['page']) ? max(1, intval($_POST['page'])) : 1;
3818 - $per_page = isset($_POST['per_page']) ? min(100, max(1, intval($_POST['per_page']))) : 50;
3819 - $offset = ($page - 1) * $per_page;
3820 - $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
3821 - $callback_filter = isset($_POST['callback_filter']) ? sanitize_text_field($_POST['callback_filter']) : '';
3822 - $sort_order = isset($_POST['sort_order']) && $_POST['sort_order'] === 'asc' ? 'ASC' : 'DESC';
3823 -
3824 - // Build WHERE clause
3825 - $where = '1=1';
3826 - $params = array();
3827 -
3828 - if ($search) {
3829 - $search_like = '%' . $wpdb->esc_like($search) . '%';
3830 - $where .= ' AND (intent_label LIKE %s OR phrases LIKE %s)';
3831 - $params[] = $search_like;
3832 - $params[] = $search_like;
3833 - }
3834 -
3835 - if ($callback_filter) {
3836 - $where .= ' AND callback_function = %s';
3837 - $params[] = $callback_filter;
3838 - }
3839 -
3840 - // Get total count
3841 - $count_query = "SELECT COUNT(*) FROM $table_name WHERE $where";
3842 - if (!empty($params)) {
3843 - $count_query = $wpdb->prepare($count_query, $params);
3844 - }
3845 - $total_actions = $wpdb->get_var($count_query);
3846 -
3847 - // Get actions
3848 - $query = "SELECT * FROM $table_name WHERE $where ORDER BY id $sort_order LIMIT %d OFFSET %d";
3849 - $all_params = array_merge($params, array($per_page, $offset));
3850 - $actions = $wpdb->get_results($wpdb->prepare($query, $all_params));
3851 -
3852 - // Get available callbacks for labels/icons
3853 - $available_callbacks = $this->mxchat_get_available_callbacks();
3854 -
3855 - // Prefetch individual phrase counts for all fetched actions
3856 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
3857 - $phrase_counts = array();
3858 - if ($wpdb->get_var("SHOW TABLES LIKE '$phrases_table'") === $phrases_table) {
3859 - $action_ids = wp_list_pluck($actions, 'id');
3860 - if (!empty($action_ids)) {
3861 - $id_placeholders = implode(',', array_fill(0, count($action_ids), '%d'));
3862 - $count_results = $wpdb->get_results($wpdb->prepare(
3863 - "SELECT intent_id, COUNT(*) as cnt FROM $phrases_table WHERE intent_id IN ($id_placeholders) GROUP BY intent_id",
3864 - $action_ids
3865 - ));
3866 - foreach ($count_results as $row) {
3867 - $phrase_counts[$row->intent_id] = intval($row->cnt);
3868 - }
3869 - }
3870 - }
3871 -
3872 - // Format actions for response
3873 - $formatted_actions = array();
3874 - foreach ($actions as $action) {
3875 - $callback_data = isset($available_callbacks[$action->callback_function])
3876 - ? $available_callbacks[$action->callback_function]
3877 - : array('label' => $action->callback_function, 'icon' => 'admin-generic');
3878 -
3879 - $enabled_bots = json_decode($action->enabled_bots, true);
3880 - if (!is_array($enabled_bots)) {
3881 - $enabled_bots = array('default');
3882 - }
3883 -
3884 - $formatted_actions[] = array(
3885 - 'id' => intval($action->id),
3886 - 'label' => $action->intent_label,
3887 - 'phrases' => $action->phrases,
3888 - 'callback_function' => $action->callback_function,
3889 - 'callback_label' => $callback_data['label'],
3890 - 'icon' => isset($callback_data['icon']) ? $callback_data['icon'] : 'admin-generic',
3891 - 'threshold' => round($action->similarity_threshold * 100),
3892 - 'enabled' => (bool) $action->enabled,
3893 - 'enabled_bots' => $enabled_bots,
3894 - 'has_legacy_vector' => !empty($action->embedding_vector),
3895 - 'individual_phrase_count' => isset($phrase_counts[$action->id]) ? $phrase_counts[$action->id] : 0,
3896 - );
3897 - }
3898 -
3899 - $total_pages = ceil($total_actions / $per_page);
3900 - $showing_start = $total_actions > 0 ? $offset + 1 : 0;
3901 - $showing_end = min($offset + $per_page, $total_actions);
3902 -
3903 - wp_send_json_success(array(
3904 - 'actions' => $formatted_actions,
3905 - 'page' => $page,
3906 - 'per_page' => $per_page,
3907 - 'total_actions' => intval($total_actions),
3908 - 'total_pages' => $total_pages,
3909 - 'showing_start' => $showing_start,
3910 - 'showing_end' => $showing_end,
3911 - ));
3912 -}
3913 -
3914 4413 /**
3915 - * AJAX handler to toggle action enabled status
3916 - */
3917 -public function mxchat_toggle_action_status() {
3918 - check_ajax_referer('mxchat_actions_nonce', 'security');
3919 -
3920 - if (!current_user_can('manage_options')) {
3921 - wp_send_json_error(__('Unauthorized', 'mxchat'));
3922 - }
3923 -
3924 - $action_id = isset($_POST['action_id']) ? intval($_POST['action_id']) : 0;
3925 - $enabled = isset($_POST['enabled']) ? intval($_POST['enabled']) : 0;
3926 -
3927 - if (!$action_id) {
3928 - wp_send_json_error(__('Invalid action ID', 'mxchat'));
3929 - }
3930 -
3931 - global $wpdb;
3932 - $table_name = $wpdb->prefix . 'mxchat_intents';
3933 -
3934 - $result = $wpdb->update(
3935 - $table_name,
3936 - array('enabled' => $enabled ? 1 : 0),
3937 - array('id' => $action_id),
3938 - array('%d'),
3939 - array('%d')
3940 - );
3941 -
3942 - if ($result === false) {
3943 - wp_send_json_error(__('Failed to update action status', 'mxchat'));
3944 - }
3945 -
3946 - wp_send_json_success(array('enabled' => (bool) $enabled));
3947 -}
3948 -
3949 -/**
3950 - * AJAX handler to bulk delete actions
3951 - */
3952 -public function mxchat_bulk_delete_actions() {
3953 - check_ajax_referer('mxchat_delete_intent_nonce', 'security');
3954 -
3955 - if (!current_user_can('manage_options')) {
3956 - wp_send_json_error(__('Unauthorized', 'mxchat'));
3957 - }
3958 -
3959 - $action_ids = isset($_POST['action_ids']) ? array_map('intval', (array) $_POST['action_ids']) : array();
3960 -
3961 - if (empty($action_ids)) {
3962 - wp_send_json_error(__('No actions selected', 'mxchat'));
3963 - }
3964 -
3965 - global $wpdb;
3966 - $table_name = $wpdb->prefix . 'mxchat_intents';
3967 -
3968 - $placeholders = implode(',', array_fill(0, count($action_ids), '%d'));
3969 - $query = $wpdb->prepare("DELETE FROM $table_name WHERE id IN ($placeholders)", $action_ids);
3970 - $result = $wpdb->query($query);
3971 -
3972 - if ($result === false) {
3973 - wp_send_json_error(__('Failed to delete actions', 'mxchat'));
3974 - }
3975 -
3976 - // Also delete individual phrases for these intents
3977 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
3978 - if ($wpdb->get_var("SHOW TABLES LIKE '$phrases_table'") === $phrases_table) {
3979 - $wpdb->query($wpdb->prepare("DELETE FROM $phrases_table WHERE intent_id IN ($placeholders)", $action_ids));
3980 - }
3981 -
3982 - wp_send_json_success(array('deleted' => $result));
3983 -}
3984 -
3985 -/**
3986 - * AJAX handler to add a new intent/action
3987 - */
3988 -public function mxchat_add_intent_ajax() {
3989 - check_ajax_referer('mxchat_add_intent_nonce', 'security');
3990 -
3991 - if (!current_user_can('manage_options')) {
3992 - wp_send_json_error(__('Unauthorized', 'mxchat'));
3993 - }
3994 -
3995 - global $wpdb;
3996 - $table_name = $wpdb->prefix . 'mxchat_intents';
3997 -
3998 - // Sanitize input
3999 - $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
4000 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
4001 - $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
4002 - $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
4003 - $enabled_bots = isset($_POST['enabled_bots']) ? array_map('sanitize_text_field', (array) $_POST['enabled_bots']) : array('default');
4004 -
4005 - // Validate
4006 - // Check if using new individual phrases mode or legacy mode
4007 - $individual_phrases = isset($_POST['individual_phrases']) ? array_filter(array_map('sanitize_text_field', (array) $_POST['individual_phrases'])) : array();
4008 - $use_individual = !empty($individual_phrases);
4009 -
4010 - // Validate required fields (phrases not required when using individual mode)
4011 - if (empty($intent_label) || empty($callback_function)) {
4012 - wp_send_json_error(__('Please fill in all required fields.', 'mxchat'));
4013 - }
4014 - if (!$use_individual && empty($phrases_input)) {
4015 - wp_send_json_error(__('Please fill in all required fields.', 'mxchat'));
4016 - }
4017 -
4018 - // Ensure default bot is included
4019 - if (!in_array('default', $enabled_bots)) {
4020 - $enabled_bots[] = 'default';
4021 - }
4022 - $enabled_bots_json = json_encode($enabled_bots);
4023 -
4024 - if ($use_individual) {
4025 - // New mode: individual phrases each get their own vector
4026 - // Insert the intent row with empty legacy fields
4027 - $result = $wpdb->insert(
4028 - $table_name,
4029 - array(
4030 - 'intent_label' => $intent_label,
4031 - 'phrases' => '',
4032 - 'embedding_vector' => '',
4033 - 'similarity_threshold' => $similarity_threshold,
4034 - 'callback_function' => $callback_function,
4035 - 'enabled' => 1,
4036 - 'enabled_bots' => $enabled_bots_json,
4037 - )
4038 - );
4039 -
4040 - if ($result === false) {
4041 - wp_send_json_error(__('Failed to add action to database.', 'mxchat'));
4042 - }
4043 -
4044 - $intent_id = $wpdb->insert_id;
4045 -
4046 - // Insert each phrase individually with its own embedding
4047 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
4048 - $failed_phrases = array();
4049 - foreach ($individual_phrases as $phrase) {
4050 - $phrase = trim($phrase);
4051 - if (empty($phrase)) continue;
4052 -
4053 - $embedding_vector = $this->mxchat_generate_embedding($phrase);
4054 - if (is_wp_error($embedding_vector)) {
4055 - $failed_phrases[] = $phrase;
4056 - continue;
4057 - }
4058 -
4059 - $wpdb->insert(
4060 - $phrases_table,
4061 - array(
4062 - 'intent_id' => $intent_id,
4063 - 'phrase' => $phrase,
4064 - 'embedding_vector' => maybe_serialize($embedding_vector),
4065 - )
4066 - );
4067 - }
4068 -
4069 - $response = array('id' => $intent_id);
4070 - if (!empty($failed_phrases)) {
4071 - $response['failed_phrases'] = $failed_phrases;
4072 - }
4073 - wp_send_json_success($response);
4074 -
4075 - } else {
4076 - // Legacy mode: combine all phrases into one embedding (backwards compatible)
4077 - $phrases_array = array_filter(array_map('trim', preg_split('/[\n,]+/', $phrases_input)));
4078 - if (empty($phrases_array)) {
4079 - wp_send_json_error(__('Please provide at least one trigger phrase.', 'mxchat'));
4080 - }
4081 -
4082 - // Generate embedding (combine phrases into single string for embedding)
4083 - $embedding_vector = $this->mxchat_generate_embedding(implode(' ', $phrases_array));
4084 - if (is_wp_error($embedding_vector)) {
4085 - // Fallback: store without embedding
4086 - $serialized_vector = null;
4087 - } else {
4088 - $serialized_vector = maybe_serialize($embedding_vector);
4089 - }
4090 -
4091 - // Insert
4092 - $result = $wpdb->insert(
4093 - $table_name,
4094 - array(
4095 - 'intent_label' => $intent_label,
4096 - 'phrases' => implode(', ', $phrases_array),
4097 - 'embedding_vector' => $serialized_vector,
4098 - 'similarity_threshold' => $similarity_threshold,
4099 - 'callback_function' => $callback_function,
4100 - 'enabled' => 1,
4101 - 'enabled_bots' => $enabled_bots_json,
4102 - )
4103 - );
4104 -
4105 - if ($result === false) {
4106 - wp_send_json_error(__('Failed to add action to database.', 'mxchat'));
4107 - }
4108 -
4109 - wp_send_json_success(array('id' => $wpdb->insert_id));
4110 - }
4111 -}
4112 -
4113 -/**
4114 - * AJAX handler to edit an existing intent/action
4115 - */
4116 -public function mxchat_edit_intent_ajax() {
4117 - check_ajax_referer('mxchat_edit_intent', 'security');
4118 -
4119 - if (!current_user_can('manage_options')) {
4120 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4121 - }
4122 -
4123 - global $wpdb;
4124 - $table_name = $wpdb->prefix . 'mxchat_intents';
4125 -
4126 - // Sanitize input
4127 - $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4128 - $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
4129 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
4130 - $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
4131 - $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
4132 - $enabled_bots = isset($_POST['enabled_bots']) ? array_map('sanitize_text_field', (array) $_POST['enabled_bots']) : array('default');
4133 -
4134 - // Validate
4135 - if (!$intent_id || empty($intent_label)) {
4136 - wp_send_json_error(__('Please fill in all required fields.', 'mxchat'));
4137 - }
4138 -
4139 - // Check if phrases are managed individually (empty phrases_input means individual mode)
4140 - $uses_individual_phrases = empty($phrases_input);
4141 -
4142 - if ($uses_individual_phrases) {
4143 - // Individual phrase mode: only update non-phrase fields, skip embedding regeneration
4144 - $update_data = array(
4145 - 'intent_label' => $intent_label,
4146 - 'similarity_threshold' => $similarity_threshold,
4147 - 'callback_function' => $callback_function,
4148 - 'enabled_bots' => json_encode($enabled_bots),
4149 - );
4150 - } else {
4151 - // Legacy mode: process phrases and regenerate embedding (backwards compatible for add-ons)
4152 - $phrases_array = array_filter(array_map('trim', preg_split('/[\n,]+/', $phrases_input)));
4153 - if (empty($phrases_array)) {
4154 - wp_send_json_error(__('Please provide at least one trigger phrase.', 'mxchat'));
4155 - }
4156 -
4157 - // Generate new embedding (combine phrases into single string for embedding)
4158 - $embedding_vector = $this->mxchat_generate_embedding(implode(' ', $phrases_array));
4159 - if (is_wp_error($embedding_vector)) {
4160 - // Keep existing embedding
4161 - $update_data = array(
4162 - 'intent_label' => $intent_label,
4163 - 'phrases' => implode(', ', $phrases_array),
4164 - 'similarity_threshold' => $similarity_threshold,
4165 - 'callback_function' => $callback_function,
4166 - 'enabled_bots' => json_encode($enabled_bots),
4167 - );
4168 - } else {
4169 - $serialized_vector = maybe_serialize($embedding_vector);
4170 - $update_data = array(
4171 - 'intent_label' => $intent_label,
4172 - 'phrases' => implode(', ', $phrases_array),
4173 - 'embedding_vector' => $serialized_vector,
4174 - 'similarity_threshold' => $similarity_threshold,
4175 - 'callback_function' => $callback_function,
4176 - 'enabled_bots' => json_encode($enabled_bots),
4177 - );
4178 - }
4179 - }
4180 -
4181 - // Ensure default bot is included
4182 - if (!in_array('default', $enabled_bots)) {
4183 - $enabled_bots[] = 'default';
4184 - }
4185 - $update_data['enabled_bots'] = json_encode($enabled_bots);
4186 -
4187 - $result = $wpdb->update(
4188 - $table_name,
4189 - $update_data,
4190 - array('id' => $intent_id),
4191 - null,
4192 - array('%d')
4193 - );
4194 -
4195 - if ($result === false) {
4196 - wp_send_json_error(__('Failed to update action.', 'mxchat'));
4197 - }
4198 -
4199 - wp_send_json_success(array('updated' => true));
4200 -}
4201 -
4202 -/**
4203 - * AJAX handler to delete a single intent/action
4204 - */
4205 -public function mxchat_delete_intent_ajax() {
4206 - check_ajax_referer('mxchat_delete_intent_nonce', 'security');
4207 -
4208 - if (!current_user_can('manage_options')) {
4209 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4210 - }
4211 -
4212 - $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4213 -
4214 - if (!$intent_id) {
4215 - wp_send_json_error(__('Invalid action ID', 'mxchat'));
4216 - }
4217 -
4218 - global $wpdb;
4219 - $table_name = $wpdb->prefix . 'mxchat_intents';
4220 -
4221 - $result = $wpdb->delete($table_name, array('id' => $intent_id), array('%d'));
4222 -
4223 - if ($result === false) {
4224 - wp_send_json_error(__('Failed to delete action', 'mxchat'));
4225 - }
4226 -
4227 - // Also delete individual phrases for this intent
4228 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
4229 - if ($wpdb->get_var("SHOW TABLES LIKE '$phrases_table'") === $phrases_table) {
4230 - $wpdb->delete($phrases_table, array('intent_id' => $intent_id), array('%d'));
4231 - }
4232 -
4233 - wp_send_json_success(array('deleted' => true));
4234 -}
4235 -
4236 -/**
4237 - * AJAX handler to add a single phrase with its own embedding to an intent
4238 - */
4239 -public function mxchat_add_phrase_ajax() {
4240 - check_ajax_referer('mxchat_add_phrase_nonce', 'security');
4241 -
4242 - if (!current_user_can('manage_options')) {
4243 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4244 - }
4245 -
4246 - $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4247 - $phrase = isset($_POST['phrase']) ? sanitize_text_field($_POST['phrase']) : '';
4248 -
4249 - if (!$intent_id || empty($phrase)) {
4250 - wp_send_json_error(__('Please provide an intent ID and phrase.', 'mxchat'));
4251 - }
4252 -
4253 - // Verify the intent exists
4254 - global $wpdb;
4255 - $intents_table = $wpdb->prefix . 'mxchat_intents';
4256 - $intent = $wpdb->get_row($wpdb->prepare("SELECT id FROM $intents_table WHERE id = %d", $intent_id));
4257 - if (!$intent) {
4258 - wp_send_json_error(__('Action not found.', 'mxchat'));
4259 - }
4260 -
4261 - // Generate embedding for this single phrase
4262 - $embedding_vector = $this->mxchat_generate_embedding($phrase);
4263 - if (is_wp_error($embedding_vector)) {
4264 - wp_send_json_error(__('Failed to generate embedding: ', 'mxchat') . $embedding_vector->get_error_message());
4265 - }
4266 -
4267 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
4268 - $result = $wpdb->insert(
4269 - $phrases_table,
4270 - array(
4271 - 'intent_id' => $intent_id,
4272 - 'phrase' => $phrase,
4273 - 'embedding_vector' => maybe_serialize($embedding_vector),
4274 - )
4275 - );
4276 -
4277 - if ($result === false) {
4278 - wp_send_json_error(__('Failed to add phrase.', 'mxchat'));
4279 - }
4280 -
4281 - wp_send_json_success(array('id' => $wpdb->insert_id, 'phrase' => $phrase));
4282 -}
4283 -
4284 -/**
4285 - * AJAX handler to delete a single phrase from wp_mxchat_intent_phrases
4286 - */
4287 -public function mxchat_delete_phrase_ajax() {
4288 - check_ajax_referer('mxchat_delete_phrase_nonce', 'security');
4289 -
4290 - if (!current_user_can('manage_options')) {
4291 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4292 - }
4293 -
4294 - $phrase_id = isset($_POST['phrase_id']) ? intval($_POST['phrase_id']) : 0;
4295 - if (!$phrase_id) {
4296 - wp_send_json_error(__('Invalid phrase ID.', 'mxchat'));
4297 - }
4298 -
4299 - global $wpdb;
4300 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
4301 - $result = $wpdb->delete($phrases_table, array('id' => $phrase_id), array('%d'));
4302 -
4303 - if ($result === false) {
4304 - wp_send_json_error(__('Failed to delete phrase.', 'mxchat'));
4305 - }
4306 -
4307 - wp_send_json_success(array('deleted' => true));
4308 -}
4309 -
4310 -/**
4311 - * AJAX handler to fetch individual phrases for an intent
4312 - */
4313 -public function mxchat_get_phrases_ajax() {
4314 - check_ajax_referer('mxchat_get_phrases_nonce', 'security');
4315 -
4316 - if (!current_user_can('manage_options')) {
4317 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4318 - }
4319 -
4320 - $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4321 - if (!$intent_id) {
4322 - wp_send_json_error(__('Invalid intent ID.', 'mxchat'));
4323 - }
4324 -
4325 - global $wpdb;
4326 - $phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
4327 -
4328 - $phrases = array();
4329 - if ($wpdb->get_var("SHOW TABLES LIKE '$phrases_table'") === $phrases_table) {
4330 - $phrases = $wpdb->get_results($wpdb->prepare(
4331 - "SELECT id, phrase, created_at FROM $phrases_table WHERE intent_id = %d ORDER BY created_at ASC",
4332 - $intent_id
4333 - ));
4334 - }
4335 -
4336 - wp_send_json_success(array('phrases' => $phrases));
4337 -}
4338 -
4339 -/**
4340 - * AJAX handler to clear legacy phrases and embedding from the main intents table
4341 - */
4342 -public function mxchat_delete_legacy_phrases_ajax() {
4343 - check_ajax_referer('mxchat_delete_legacy_nonce', 'security');
4344 -
4345 - if (!current_user_can('manage_options')) {
4346 - wp_send_json_error(__('Unauthorized', 'mxchat'));
4347 - }
4348 -
4349 - $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
4350 - if (!$intent_id) {
4351 - wp_send_json_error(__('Invalid intent ID.', 'mxchat'));
4352 - }
4353 -
4354 - global $wpdb;
4355 - $table_name = $wpdb->prefix . 'mxchat_intents';
4356 -
4357 - $result = $wpdb->update(
4358 - $table_name,
4359 - array('phrases' => '', 'embedding_vector' => ''),
4360 - array('id' => $intent_id),
4361 - array('%s', '%s'),
4362 - array('%d')
4363 - );
4364 -
4365 - if ($result === false) {
4366 - wp_send_json_error(__('Failed to clear legacy phrases.', 'mxchat'));
4367 - }
4368 -
4369 - wp_send_json_success(array('cleared' => true));
4370 -}
4371 -
4372 -/**
4373 4414 * Enhanced get_available_callbacks function with form action exclusion
4374 4415 *
4375 4416 * @param bool $grouped Whether to return callbacks grouped by category
4376 4417 * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
@@ -4408,10 +4449,9 @@
4408 4449 'mxchat_handle_product_recommendations',
4409 4450 'mxchat_handle_order_history',
4410 4451 'mxchat_show_product_card',
4411 4452 'mxchat_add_to_cart',
4412 - 'mxchat_checkout_redirect',
4413 - 'mxchat_handle_featured_products'
4453 + 'mxchat_checkout_redirect'
4414 4454 ),
4415 4455 'name' => __('WooCommerce Add-on', 'mxchat'),
4416 4456 'pro_required' => true
4417 4457 ),
@@ -4489,25 +4529,16 @@
4489 4529 'installed' => true
4490 4530 ),
4491 4531 // Pro core features - check is_activated property
4492 4532 'mxchat_generate_image' => array(
4493 - 'label' => __('Generate Image (OpenAI)', 'mxchat'),
4533 + 'label' => __('Generate Image', 'mxchat'),
4494 4534 'pro_only' => false,
4495 4535 'group' => __('Other Features', 'mxchat'),
4496 4536 'icon' => 'art',
4497 - 'description' => __('Create images with GPT Image from OpenAI (requires OpenAI API key)', 'mxchat'),
4537 + 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
4498 4538 'addon' => false,
4499 4539 'installed' => true
4500 4540 ),
4501 - 'mxchat_generate_gemini_image' => array(
4502 - 'label' => __('Generate Image (Gemini)', 'mxchat'),
4503 - 'pro_only' => false,
4504 - 'group' => __('Other Features', 'mxchat'),
4505 - 'icon' => 'art',
4506 - 'description' => __('Create images with Imagen from Google (requires Gemini API key)', 'mxchat'),
4507 - 'addon' => false,
4508 - 'installed' => true
4509 - ),
4510 4541 'mxchat_handle_pdf_discussion' => array(
4511 4542 'label' => __('Chat with PDF', 'mxchat'),
4512 4543 'pro_only' => false,
4513 4544 'group' => __('Other Features', 'mxchat'),
@@ -4524,17 +4555,8 @@
4524 4555 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
4525 4556 'addon' => false,
4526 4557 'installed' => true
4527 4558 ),
4528 - 'mxchat_telegram_live_agent_handover' => array(
4529 - 'label' => __('Telegram Live Agent', 'mxchat'),
4530 - 'pro_only' => false,
4531 - 'group' => __('Customer Engagement', 'mxchat'),
4532 - 'icon' => 'format-chat',
4533 - 'description' => __('Transfer conversation to a human support agent on Telegram', 'mxchat'),
4534 - 'addon' => false,
4535 - 'installed' => true
4536 - ),
4537 4559 'mxchat_handle_switch_to_chatbot_intent' => array(
4538 4560 'label' => __('Back to Chatbot', 'mxchat'),
4539 4561 'pro_only' => false,
4540 4562 'group' => __('Customer Engagement', 'mxchat'),
@@ -4545,15 +4567,13 @@
4545 4567 ),
4546 4568 );
4547 4569
4548 4570 // Add-on callbacks with placeholders - only include if the add-on is NOT active
4549 - // These are promotional/informational only — not selectable as real actions
4550 4571 $addon_callbacks = array(
4551 4572 // WooCommerce Add-on
4552 4573 'mxchat_handle_product_recommendations' => array(
4553 4574 'label' => __('Product Recommendations', 'mxchat'),
4554 - 'pro_only' => false,
4555 - 'addon_promo' => true,
4575 + 'pro_only' => true,
4556 4576 'group' => __('WooCommerce Features', 'mxchat'),
4557 4577 'icon' => 'cart',
4558 4578 'description' => __('Suggest products based on customer preferences', 'mxchat'),
4559 4579 ),
@@ -4558,10 +4578,9 @@
4558 4578 'description' => __('Suggest products based on customer preferences', 'mxchat'),
4559 4579 ),
4560 4580 'mxchat_handle_order_history' => array(
4561 4581 'label' => __('Order History', 'mxchat'),
4562 - 'pro_only' => false,
4563 - 'addon_promo' => true,
4582 + 'pro_only' => true,
4564 4583 'group' => __('WooCommerce Features', 'mxchat'),
4565 4584 'icon' => 'clipboard',
4566 4585 'description' => __('Allow customers to check their order status', 'mxchat'),
4567 4586 ),
@@ -4566,10 +4585,9 @@
4566 4585 'description' => __('Allow customers to check their order status', 'mxchat'),
4567 4586 ),
4568 4587 'mxchat_show_product_card' => array(
4569 4588 'label' => __('Show Product Card', 'mxchat'),
4570 - 'pro_only' => false,
4571 - 'addon_promo' => true,
4589 + 'pro_only' => true,
4572 4590 'group' => __('WooCommerce Features', 'mxchat'),
4573 4591 'icon' => 'products',
4574 4592 'description' => __('Display product information in the chat', 'mxchat'),
4575 4593 ),
@@ -4574,10 +4592,9 @@
4574 4592 'description' => __('Display product information in the chat', 'mxchat'),
4575 4593 ),
4576 4594 'mxchat_add_to_cart' => array(
4577 4595 'label' => __('Add to Cart', 'mxchat'),
4578 - 'pro_only' => false,
4579 - 'addon_promo' => true,
4596 + 'pro_only' => true,
4580 4597 'group' => __('WooCommerce Features', 'mxchat'),
4581 4598 'icon' => 'plus-alt',
4582 4599 'description' => __('Add products to cart directly from chat', 'mxchat'),
4583 4600 ),
@@ -4582,38 +4599,27 @@
4582 4599 'description' => __('Add products to cart directly from chat', 'mxchat'),
4583 4600 ),
4584 4601 'mxchat_checkout_redirect' => array(
4585 4602 'label' => __('Proceed to Checkout', 'mxchat'),
4586 - 'pro_only' => false,
4587 - 'addon_promo' => true,
4603 + 'pro_only' => true,
4588 4604 'group' => __('WooCommerce Features', 'mxchat'),
4589 4605 'icon' => 'arrow-right-alt',
4590 4606 'description' => __('Redirect customer to checkout page', 'mxchat'),
4591 4607 ),
4592 - 'mxchat_handle_featured_products' => array(
4593 - 'label' => __('Featured Products Showcase', 'mxchat'),
4594 - 'pro_only' => false,
4595 - 'addon_promo' => true,
4596 - 'group' => __('WooCommerce Features', 'mxchat'),
4597 - 'icon' => 'star-filled',
4598 - 'description' => __('Display a curated selection of products with an AI-generated message', 'mxchat'),
4599 - ),
4600 4608
4601 4609 // Perplexity Add-on
4602 4610 'mxchat_perplexity_research' => array(
4603 4611 'label' => __('Perplexity Research', 'mxchat'),
4604 - 'pro_only' => false,
4605 - 'addon_promo' => true,
4612 + 'pro_only' => true,
4606 4613 'group' => __('Search Features', 'mxchat'),
4607 4614 'icon' => 'book-alt',
4608 4615 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
4609 4616 ),
4610 4617
4611 - // Forms Add-on
4618 + // Forms Add-on (only shown when Pro is not activated)
4612 4619 'mxchat_handle_form_collection' => array(
4613 4620 'label' => __('Form Collection', 'mxchat'),
4614 - 'pro_only' => false,
4615 - 'addon_promo' => true,
4621 + 'pro_only' => true,
4616 4622 'group' => __('Form Features', 'mxchat'),
4617 4623 'icon' => 'feedback',
4618 4624 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
4619 4625 ),
@@ -4867,27 +4873,9 @@
4867 4873 'mxchat-chatbot', // Page
4868 4874 'mxchat_chatbot_section' // Section
4869 4875 );
4870 4876
4871 - // RAG Sources Limit Slider
4872 4877 add_settings_field(
4873 - 'rag_sources_limit', // Field ID
4874 - esc_html__('RAG Sources Limit', 'mxchat'), // Field title
4875 - array($this, 'mxchat_rag_sources_limit_callback'), // Callback function
4876 - 'mxchat-chatbot', // Page
4877 - 'mxchat_chatbot_section' // Section
4878 - );
4879 -
4880 - // RAG Chunks Limit Slider
4881 - add_settings_field(
4882 - 'rag_chunks_limit', // Field ID
4883 - esc_html__('RAG Chunks Limit', 'mxchat'), // Field title
4884 - array($this, 'mxchat_rag_chunks_limit_callback'), // Callback function
4885 - 'mxchat-chatbot', // Page
4886 - 'mxchat_chatbot_section' // Section
4887 - );
4888 -
4889 - add_settings_field(
4890 4878 'append_to_body',
4891 4879 esc_html__('Auto-Display Chatbot', 'mxchat'),
4892 4880 array($this, 'mxchat_append_to_body_callback'),
4893 4881 'mxchat-chatbot',
@@ -4902,16 +4890,8 @@
4902 4890 'mxchat_chatbot_section'
4903 4891 );
4904 4892
4905 4893 add_settings_field(
4906 - 'citation_links_toggle',
4907 - esc_html__('Citation Links', 'mxchat'),
4908 - array($this, 'mxchat_citation_links_toggle_callback'),
4909 - 'mxchat-chatbot',
4910 - 'mxchat_chatbot_section'
4911 - );
4912 -
4913 - add_settings_field(
4914 4894 'enable_streaming_toggle',
4915 4895 esc_html__('Enable Streaming', 'mxchat'),
4916 4896 array($this, 'enable_streaming_toggle_callback'),
4917 4897 'mxchat-chatbot',
@@ -5315,64 +5295,11 @@
5315 5295 'mxchat-embed',
5316 5296 'mxchat_live_agent_section'
5317 5297 );
5318 5298
5319 - // Telegram Integration Section
5320 - add_settings_section(
5321 - 'mxchat_telegram_section',
5322 - __('Telegram Settings', 'mxchat'),
5323 - array($this, 'mxchat_telegram_section_callback'),
5324 - 'mxchat-embed'
5325 - );
5326 5299
5327 - add_settings_field(
5328 - 'telegram_status',
5329 - __('Live Agent Status', 'mxchat'),
5330 - array($this, 'mxchat_telegram_status_callback'),
5331 - 'mxchat-embed',
5332 - 'mxchat_telegram_section'
5333 - );
5334 5300
5335 - add_settings_field(
5336 - 'telegram_notification_message',
5337 - __('Notification Message', 'mxchat'),
5338 - array($this, 'mxchat_telegram_notification_message_callback'),
5339 - 'mxchat-embed',
5340 - 'mxchat_telegram_section'
5341 - );
5342 5301
5343 - add_settings_field(
5344 - 'telegram_away_message',
5345 - __('Away Message', 'mxchat'),
5346 - array($this, 'mxchat_telegram_away_message_callback'),
5347 - 'mxchat-embed',
5348 - 'mxchat_telegram_section'
5349 - );
5350 -
5351 - add_settings_field(
5352 - 'telegram_bot_token',
5353 - __('Telegram Bot Token', 'mxchat'),
5354 - array($this, 'mxchat_telegram_bot_token_callback'),
5355 - 'mxchat-embed',
5356 - 'mxchat_telegram_section'
5357 - );
5358 -
5359 - add_settings_field(
5360 - 'telegram_group_id',
5361 - __('Telegram Group ID', 'mxchat'),
5362 - array($this, 'mxchat_telegram_group_id_callback'),
5363 - 'mxchat-embed',
5364 - 'mxchat_telegram_section'
5365 - );
5366 -
5367 - add_settings_field(
5368 - 'telegram_webhook_secret',
5369 - __('Webhook Secret Token', 'mxchat'),
5370 - array($this, 'mxchat_telegram_webhook_secret_callback'),
5371 - 'mxchat-embed',
5372 - 'mxchat_telegram_section'
5373 - );
5374 -
5375 5302 // General Settings Section
5376 5303 add_settings_section(
5377 5304 'mxchat_general_section',
5378 5305 esc_html__('YouTube Tutorials', 'mxchat'),
@@ -5532,9 +5459,8 @@
5532 5459
5533 5460 ?>
5534 5461 <div class="notice notice-success is-dismissible">
5535 5462 <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
5536 - <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e('Dismiss this notice.', 'mxchat'); ?></span></button>
5537 5463 </div>
5538 5464 <?php
5539 5465
5540 5466 }
@@ -5683,12 +5609,11 @@
5683 5609
5684 5610 // OpenAI API Key
5685 5611 public function api_key_callback() {
5686 5612 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
5687 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5688 5613
5689 5614 echo '<div class="api-key-wrapper">';
5690 - echo '<input type="text" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5615 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5691 5616 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5692 5617 echo '<p class="description">' . esc_html__('Required for OpenAI GPT models and OpenAI embeddings. Get your API key from OpenAI Platform.', 'mxchat') . '</p>';
5693 5618 echo '</div>';
5694 5619 }
@@ -5695,12 +5620,11 @@
5695 5620
5696 5621 // X.AI API Key
5697 5622 public function xai_api_key_callback() {
5698 5623 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
5699 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5700 5624
5701 5625 echo '<div class="api-key-wrapper">';
5702 - echo '<input type="text" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5626 + echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
5703 5627 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5704 5628 echo '<p class="description">' . esc_html__('Required for X.AI Grok models. Get your API key from X.AI Console.', 'mxchat') . '</p>';
5705 5629 echo '</div>';
5706 5630 }
@@ -5706,12 +5630,11 @@
5706 5630 }
5707 5631 // Claude API Key
5708 5632 public function claude_api_key_callback() {
5709 5633 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
5710 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5711 5634
5712 5635 echo '<div class="api-key-wrapper">';
5713 - echo '<input type="text" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5636 + echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
5714 5637 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5715 5638 echo '<p class="description">' . esc_html__('Required for Anthropic Claude models. Get your API key from Anthropic Console.', 'mxchat') . '</p>';
5716 5639 echo '</div>';
5717 5640 }
@@ -5718,12 +5641,11 @@
5718 5641
5719 5642 // DeepSeek API Key
5720 5643 public function deepseek_api_key_callback() {
5721 5644 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
5722 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5723 5645
5724 5646 echo '<div class="api-key-wrapper">';
5725 - echo '<input type="text" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5647 + echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5726 5648 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5727 5649 echo '<p class="description">' . esc_html__('Required for DeepSeek models. Get your API key from DeepSeek Platform.', 'mxchat') . '</p>';
5728 5650 echo '</div>';
5729 5651 }
@@ -5730,12 +5652,11 @@
5730 5652
5731 5653 // Gemini API Key
5732 5654 public function gemini_api_key_callback() {
5733 5655 $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
5734 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5735 5656
5736 5657 echo '<div class="api-key-wrapper">';
5737 - echo '<input type="text" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5658 + echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
5738 5659 echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5739 5660 echo '<p class="description">' . esc_html__('Required for Google Gemini models and embeddings. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
5740 5661 echo '</div>';
5741 5662 }
@@ -5743,12 +5664,10 @@
5743 5664
5744 5665 // OpenRouter API Key
5745 5666 public function openrouter_api_key_callback() {
5746 5667 $openrouterApiKey = isset($this->options['openrouter_api_key']) ? esc_attr($this->options['openrouter_api_key']) : '';
5747 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5748 -
5749 5668 echo '<div class="api-key-wrapper">';
5750 - echo '<input type="text" id="openrouter_api_key" name="openrouter_api_key" value="' . $openrouterApiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5669 + echo '<input type="password" id="openrouter_api_key" name="openrouter_api_key" value="' . $openrouterApiKey . '" class="regular-text" autocomplete="off" />';
5751 5670 echo '<button type="button" id="toggleOpenRouterApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5752 5671 echo '<p class="description">' . esc_html__('Required for OpenRouter models. Get your API key from OpenRouter.ai', 'mxchat') . '</p>';
5753 5672 echo '</div>';
5754 5673 }
@@ -5755,12 +5674,11 @@
5755 5674
5756 5675 // Voyage API Key
5757 5676 public function voyage_api_key_callback() {
5758 5677 $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
5759 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5760 5678
5761 5679 echo '<div class="api-key-wrapper">';
5762 - echo '<input type="text" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="' . $nonce . '" />';
5680 + echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5763 5681 echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5764 5682 echo '<p class="description">' . esc_html__('Required for Voyage AI embedding models. Get your API key from Voyage AI.', 'mxchat') . '</p>';
5765 5683 echo '</div>';
5766 5684 }
@@ -5766,15 +5684,17 @@
5766 5684 }
5767 5685
5768 5686 public function mxchat_loops_api_key_callback() {
5769 5687 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
5770 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5771 5688
5689 + // Hidden fields to "trap" autofill
5690 + echo '<input type="text" style="display:none" autocomplete="username" />';
5691 + echo '<input type="password" style="display:none" autocomplete="current-password" />';
5692 +
5772 5693 echo '<div class="api-key-wrapper">';
5773 5694 echo sprintf(
5774 - '<input type="text" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="%s" />',
5775 - $loops_api_key,
5776 - $nonce
5695 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
5696 + $loops_api_key
5777 5697 );
5778 5698 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5779 5699 echo '</div>';
5780 5700 echo '<p class="description">' . esc_html__('Required for Loops email integration. Get your API key from Loops.so', 'mxchat') . '</p>';
@@ -5782,9 +5702,8 @@
5782 5702 public function mxchat_loops_mailing_list_callback() {
5783 5703 // Add error handling and type checking
5784 5704 $loops_api_key = '';
5785 5705 $selected_list = '';
5786 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5787 5706
5788 5707 // Safely get the API key
5789 5708 if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
5790 5709 $loops_api_key = $this->options['loops_api_key'];
@@ -5797,10 +5716,9 @@
5797 5716
5798 5717 if (!empty($loops_api_key)) {
5799 5718 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
5800 5719 if (is_array($lists) && !empty($lists)) {
5801 - echo '<div class="mxchat-field-wrapper">';
5802 - echo '<select id="loops_mailing_list" name="loops_mailing_list" class="mxchat-autosave-field" data-nonce="' . $nonce . '">';
5720 + echo '<select id="loops_mailing_list" name="loops_mailing_list">';
5803 5721
5804 5722 // Add a default "Select a list" option
5805 5723 echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
5806 5724
@@ -5814,9 +5732,8 @@
5814 5732 );
5815 5733 }
5816 5734 }
5817 5735 echo '</select>';
5818 - echo '</div>';
5819 5736 echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
5820 5737 } else {
5821 5738 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
5822 5739 }
@@ -5828,17 +5745,12 @@
5828 5745 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
5829 5746 $triggered_response = isset($this->options['triggered_phrase_response'])
5830 5747 ? $this->options['triggered_phrase_response']
5831 5748 : $default_response;
5832 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5833 -
5834 - echo '<div class="mxchat-field-wrapper">';
5835 5749 echo sprintf(
5836 - '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50" class="mxchat-autosave-field" data-nonce="%s">%s</textarea>',
5837 - $nonce,
5750 + '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
5838 5751 esc_textarea($triggered_response)
5839 5752 );
5840 - echo '</div>';
5841 5753 echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a trigger keyword is detected. The AI will use this as guidance to naturally ask for the user\'s email in a conversational way.', 'mxchat') . '</p>';
5842 5754 }
5843 5755
5844 5756 public function mxchat_email_capture_response_callback() {
@@ -5845,17 +5757,12 @@
5845 5757 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
5846 5758 $email_capture_response = isset($this->options['email_capture_response'])
5847 5759 ? $this->options['email_capture_response']
5848 5760 : $default_response;
5849 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
5850 -
5851 - echo '<div class="mxchat-field-wrapper">';
5852 5761 echo sprintf(
5853 - '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50" class="mxchat-autosave-field" data-nonce="%s">%s</textarea>',
5854 - $nonce,
5762 + '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
5855 5763 esc_textarea($email_capture_response)
5856 5764 );
5857 - echo '</div>';
5858 5765 echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a user provides their email. The AI will use this as guidance to naturally confirm the email capture in a conversational way.', 'mxchat') . '</p>';
5859 5766 }
5860 5767 public function mxchat_pre_chat_message_callback() {
5861 5768 // Load the entire 'mxchat_options' array
@@ -5869,8 +5776,9 @@
5869 5776 printf(
5870 5777 '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
5871 5778 esc_textarea($pre_chat_message)
5872 5779 );
5780 + echo '<p class="description">' . esc_html__('Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.', 'mxchat') . '</p>';
5873 5781 }
5874 5782
5875 5783 // Callback for AI Instructions textarea
5876 5784 public function system_prompt_instructions_callback() {
@@ -5880,14 +5788,10 @@
5880 5788 printf(
5881 5789 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
5882 5790 $instructions
5883 5791 );
5884 - // Personalization hint
5885 - echo '<p class="description" style="margin-top: 8px;">';
5886 - echo esc_html__('Use {visitor_name} to personalize AI responses when lead capture is enabled.', 'mxchat') . '<br>';
5887 - echo '<code style="font-size: 12px;">' . esc_html__('Example: The visitor\'s name is {visitor_name}. Address them by name.', 'mxchat') . '</code>';
5888 - echo '</p>';
5889 - // Sample instructions button
5792 + // Provide a helpful description with sample instructions button
5793 + echo '<p class="description">' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '</p>';
5890 5794 echo '<div class="mxchat-instructions-container">';
5891 5795 echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
5892 5796 echo '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5893 5797 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
@@ -5968,18 +5872,19 @@
5968 5872 esc_html__('OpenRouter (100+ Models)', 'mxchat') => array(
5969 5873 'openrouter' => esc_html__('OpenRouter - Select after entering API key', 'mxchat'),
5970 5874 ),
5971 5875 esc_html__('Google Gemini Models', 'mxchat') => array(
5972 - 'gemini-3-pro-preview' => esc_html__('Gemini 3 Pro (Most Intelligent, Multimodal)', 'mxchat'),
5973 - 'gemini-3-flash-preview' => esc_html__('Gemini 3 Flash (Balanced Speed & Scale)', 'mxchat'),
5974 - 'gemini-2.5-pro' => esc_html__('Gemini 2.5 Pro (Advanced Thinking)', 'mxchat'),
5975 - 'gemini-2.5-flash' => esc_html__('Gemini 2.5 Flash (Best Price-Performance)', 'mxchat'),
5976 - 'gemini-2.5-flash-lite' => esc_html__('Gemini 2.5 Flash-Lite (Ultra Fast)', 'mxchat'),
5977 - 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Deprecated Mar 2026)', 'mxchat'),
5978 - 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Deprecated Mar 2026)', 'mxchat'),
5979 - 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Deprecated Sep 2025)', 'mxchat'),
5980 - 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Deprecated Sep 2025)', 'mxchat'),
5876 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5877 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5878 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5879 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5981 5880 ),
5881 + esc_html__('Google Gemini Models', 'mxchat') => array(
5882 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5883 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5884 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5885 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5886 + ),
5982 5887 esc_html__('X.AI Models', 'mxchat') => array(
5983 5888 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
5984 5889 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
5985 5890 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
@@ -5992,33 +5897,37 @@
5992 5897 esc_html__('DeepSeek Models', 'mxchat') => array(
5993 5898 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
5994 5899 ),
5995 5900 esc_html__('Claude Models', 'mxchat') => array(
5996 - 'claude-opus-4-6' => esc_html__('Claude Opus 4.6 (Most Capable - Recommended)', 'mxchat'),
5997 - 'claude-sonnet-4-6' => esc_html__('Claude Sonnet 4.6 (Latest Sonnet - Fast & Capable)', 'mxchat'),
5998 - 'claude-opus-4-5' => esc_html__('Claude Opus 4.5 (Highly Capable)', 'mxchat'),
5999 5901 'claude-sonnet-4-5-20250929' => esc_html__('Claude Sonnet 4.5 (Best for Agents & Coding)', 'mxchat'),
6000 5902 'claude-opus-4-1-20250805' => esc_html__('Claude Opus 4.1 (Exceptional for Complex Tasks)', 'mxchat'),
6001 5903 'claude-haiku-4-5-20251001' => esc_html__('Claude Haiku 4.5 (Fastest & Most Intelligent)', 'mxchat'),
6002 - 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Complex Tasks)', 'mxchat'),
5904 + 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
6003 5905 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
5906 + 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
5907 + 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
5908 + 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
5909 + 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat'),
6004 5910 ),
6005 5911 esc_html__('OpenAI Models', 'mxchat') => array(
6006 - 'gpt-5.4' => esc_html__('GPT-5.4 (Flagship Reasoning & Coding)', 'mxchat'),
6007 - 'gpt-5.4-mini' => esc_html__('GPT-5.4 Mini (Fast, 400K Context)', 'mxchat'),
6008 - 'gpt-5.4-nano' => esc_html__('GPT-5.4 Nano (Fastest & Cheapest)', 'mxchat'),
6009 - 'gpt-5.3-chat-latest' => esc_html__('GPT-5.3 Chat (Conversational)', 'mxchat'),
6010 - 'gpt-5.2' => esc_html__('GPT-5.2 (Best General-Purpose & Agentic Model)', 'mxchat'),
6011 - 'gpt-5.1-chat-latest' => esc_html__('GPT-5.1 Chat Latest (Recommended)', 'mxchat'),
5912 + // GPT-5 family
6012 5913 'gpt-5.1-2025-11-13' => esc_html__('GPT-5.1 (Flagship for Coding & Agentic Tasks)', 'mxchat'),
6013 5914 'gpt-5' => esc_html__('GPT-5 (Flagship for Coding, Reasoning & Agents)', 'mxchat'),
6014 - 'gpt-5-mini' => esc_html__('GPT-5 Mini (Fast and Lightweight)', 'mxchat'),
5915 + 'gpt-5-mini' => esc_html__('GPT-5 Mini (Faster, Cost-Efficient for Precise Prompts)', 'mxchat'),
6015 5916 'gpt-5-nano' => esc_html__('GPT-5 Nano (Fastest & Cheapest for Summarization/Classification)', 'mxchat'),
5917 +
5918 + // Existing OpenAI models
5919 + 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
5920 + 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
5921 + 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
5922 + 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
5923 + 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
5924 + 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat'),
6016 5925 ),
6017 5926 );
6018 5927
6019 5928 // Retrieve the currently selected model from saved options
6020 - $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-5.1-chat-latest';
5929 + $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
6021 5930
6022 5931 // Begin the select dropdown
6023 5932 echo '<select id="model" name="model">';
6024 5933
@@ -6034,8 +5943,10 @@
6034 5943 }
6035 5944
6036 5945 echo '</select>';
6037 5946
5947 + echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
5948 +
6038 5949 // Add a note for OpenRouter
6039 5950 echo '<p class="description" id="openrouter-model-note" style="display:none; color: #d63638; font-weight: 500;">';
6040 5951 echo '<span class="dashicons dashicons-info" style="font-size: 16px; vertical-align: middle;"></span> ';
6041 5952 echo esc_html__('After entering your OpenRouter API key above, click the button below to load available models.', 'mxchat');
@@ -6123,8 +6034,11 @@
6123 6034 esc_attr($checked)
6124 6035 );
6125 6036 echo '<span class="slider"></span>';
6126 6037 echo '</label>';
6038 + echo '<p class="description">' .
6039 + esc_html__('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, and Grok). When disabled, responses will load all at once.', 'mxchat') .
6040 + '</p>';
6127 6041
6128 6042 // Test button with better styling
6129 6043 echo '<div style="margin-top: 15px;">';
6130 6044 echo '<button type="button" id="mxchat-test-streaming-btn" class="button button-secondary">';
@@ -6134,50 +6048,8 @@
6134 6048 echo '<p id="mxchat-test-streaming-result" style="margin-top:10px; font-weight: bold;"></p>';
6135 6049 echo '</div>';
6136 6050 }
6137 6051
6138 -// Web Search toggle callback
6139 -public function enable_web_search_toggle_callback() {
6140 - // Get value from options array, default to 'off'
6141 - $enabled = isset($this->options['enable_web_search']) ? $this->options['enable_web_search'] : 'off';
6142 - $checked = ($enabled === 'on') ? 'checked' : '';
6143 -
6144 - // Get current model to determine if we should show/enable the toggle
6145 - $current_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-5.1-chat-latest';
6146 -
6147 - // Models that DON'T support web search (per OpenAI docs)
6148 - // gpt-5 with minimal reasoning is handled at API level, gpt-4.1-nano doesn't support it
6149 - $unsupported_models = array('gpt-4.1-nano');
6150 -
6151 - // Check if current model is an OpenAI model that supports web search
6152 - $openai_models = array(
6153 - 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gpt-5.3-chat-latest',
6154 - 'gpt-5.2', 'gpt-5.1-chat-latest', 'gpt-5.1-2025-11-13', 'gpt-5', 'gpt-5-mini', 'gpt-5-nano'
6155 - );
6156 -
6157 - $is_openai = in_array($current_model, $openai_models);
6158 - $is_supported = $is_openai && !in_array($current_model, $unsupported_models);
6159 -
6160 - // Wrapper div with data attributes for JS to show/hide
6161 - echo '<div id="web-search-toggle-wrapper" data-openai-models="' . esc_attr(implode(',', $openai_models)) . '" data-unsupported-models="' . esc_attr(implode(',', $unsupported_models)) . '"' . (!$is_supported ? ' style="display:none;"' : '') . '>';
6162 -
6163 - echo '<label class="toggle-switch">';
6164 - echo sprintf(
6165 - '<input type="checkbox" id="enable_web_search" name="enable_web_search" value="on" %s />',
6166 - esc_attr($checked)
6167 - );
6168 - echo '<span class="slider"></span>';
6169 - echo '</label>';
6170 -
6171 - echo '</div>';
6172 -
6173 - // Message shown when non-OpenAI model is selected
6174 - echo '<p id="web-search-unavailable-message" class="description" style="color: #666;' . ($is_supported ? ' display:none;' : '') . '">';
6175 - echo '<span class="dashicons dashicons-info" style="font-size: 16px; vertical-align: middle; margin-right: 4px;"></span>';
6176 - echo esc_html__('Web search is only available for OpenAI models.', 'mxchat');
6177 - echo '</p>';
6178 -}
6179 -
6180 6052 // AJAX handler to fetch OpenRouter models
6181 6053 public function fetch_openrouter_models() {
6182 6054 check_ajax_referer('mxchat_fetch_openrouter_models', 'nonce');
6183 6055
@@ -6234,9 +6106,9 @@
6234 6106 esc_html__('Voyage AI Embeddings', 'mxchat') => array(
6235 6107 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
6236 6108 ),
6237 6109 esc_html__('Google Gemini Embeddings', 'mxchat') => array(
6238 - 'gemini-embedding-001' => esc_html__('Gemini Embedding (1536, Stable)', 'mxchat'),
6110 + 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
6239 6111 )
6240 6112 );
6241 6113 $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
6242 6114 echo '<select id="embedding_model" name="embedding_model">';
@@ -6247,8 +6119,9 @@
6247 6119 }
6248 6120 echo '</optgroup>';
6249 6121 }
6250 6122 echo '</select>';
6123 + echo '<p class="description"><span class="red-warning">IMPORTANT:</span> A vector embedding model is required for MxChat to function. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
6251 6124
6252 6125 // API Key Status Messages for Embedding Models
6253 6126 $has_openai_key = !empty($this->options['api_key']);
6254 6127 $has_voyage_key = !empty($this->options['voyage_api_key']);
@@ -6288,8 +6161,11 @@
6288 6161 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
6289 6162
6290 6163 // Render the input field
6291 6164 echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
6165 +
6166 + // Add a description
6167 + echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
6292 6168 }
6293 6169 public function mxchat_ai_agent_text_callback() {
6294 6170 // Retrieve the current value of the AI agent text from saved options
6295 6171 $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
@@ -6294,8 +6170,10 @@
6294 6170 // Retrieve the current value of the AI agent text from saved options
6295 6171 $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
6296 6172 // Render the input field
6297 6173 echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
6174 + // Add a description
6175 + echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
6298 6176 }
6299 6177
6300 6178
6301 6179 public function enable_email_block_callback() {
@@ -6314,8 +6192,9 @@
6314 6192 esc_attr($checked)
6315 6193 );
6316 6194 echo '<span class="slider"></span>';
6317 6195 echo '</label>';
6196 + echo '<p class="description">' . esc_html__('Their email will appear at the top of the transcript. Email form will show for users who are not logged in or have not provided an email within 24h.', 'mxchat') . '</p>';
6318 6197 }
6319 6198
6320 6199 public function email_blocker_header_content_callback() {
6321 6200 // Load the entire 'mxchat_options' array
@@ -6333,8 +6212,12 @@
6333 6212 rows="5"
6334 6213 cols="70"
6335 6214 data-setting="email_blocker_header_content"
6336 6215 >' . esc_textarea($content) . '</textarea>';
6216 +
6217 + echo '<p class="description">';
6218 + echo esc_html__('You may enter HTML here, such as &lt;h2&gt;Welcome&lt;/h2&gt; or &lt;p&gt;Let\'s get started&lt;/p&gt;.', 'mxchat');
6219 + echo '</p>';
6337 6220 }
6338 6221
6339 6222 public function email_blocker_button_text_callback() {
6340 6223 // Load the entire 'mxchat_options' array
@@ -6346,8 +6229,12 @@
6346 6229 : '';
6347 6230
6348 6231 // Use esc_attr to safely render the existing text
6349 6232 echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
6233 +
6234 + echo '<p class="description">';
6235 + echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
6236 + echo '</p>';
6350 6237 }
6351 6238
6352 6239 //Enable name field callback
6353 6240 public function enable_name_field_callback() {
@@ -6363,8 +6250,9 @@
6363 6250 esc_attr($checked)
6364 6251 );
6365 6252 echo '<span class="slider"></span>';
6366 6253 echo '</label>';
6254 + echo '<p class="description">' . esc_html__('Enable this to also require users to enter their name along with their email before chatting.', 'mxchat') . '</p>';
6367 6255 }
6368 6256 //Name field placeholder callback
6369 6257 public function name_field_placeholder_callback() {
6370 6258 $all_options = get_option('mxchat_options', []);
@@ -6372,8 +6260,11 @@
6372 6260 ? $all_options['name_field_placeholder']
6373 6261 : esc_html__('Enter your name', 'mxchat');
6374 6262
6375 6263 echo '<input type="text" id="name_field_placeholder" name="name_field_placeholder" value="' . esc_attr($placeholder) . '" style="width: 300px;" />';
6264 + echo '<p class="description">';
6265 + echo esc_html__('Placeholder text for the name field.', 'mxchat');
6266 + echo '</p>';
6376 6267 }
6377 6268
6378 6269
6379 6270
@@ -6385,11 +6276,10 @@
6385 6276 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
6386 6277 // Output the textarea with the saved value without escaping HTML
6387 6278 ?>
6388 6279 <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
6389 - <p class="description" style="margin-top: 8px;">
6390 - <?php esc_html_e('Use {visitor_name} to personalize greetings when lead capture is enabled.', 'mxchat'); ?><br>
6391 - <code style="font-size: 12px;"><?php esc_html_e('Example: Hello {visitor_name}! How can I help you today?', 'mxchat'); ?></code>
6280 + <p class="description">
6281 + <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
6392 6282 </p>
6393 6283 <?php
6394 6284 }
6395 6285
@@ -6406,117 +6296,478 @@
6406 6296 '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
6407 6297 esc_attr($input_copy),
6408 6298 esc_attr__('How can I assist?', 'mxchat')
6409 6299 );
6300 +
6301 + // Output the description
6302 + echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
6410 6303 }
6411 6304
6412 6305
6413 -public function mxchat_append_to_body_callback() {
6414 - // Fetch fresh options to ensure we have the latest saved values
6415 - $options = get_option('mxchat_options', array());
6416 6306
6417 - // Get value from options array, default to 'off'
6418 - $append_to_body = isset($options['append_to_body']) ? $options['append_to_body'] : 'off';
6419 - $checked = ($append_to_body === 'on') ? 'checked' : '';
6307 +public function mxchat_user_message_font_color_callback() {
6308 + $disabled = $this->is_activated ? '' : 'disabled';
6309 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6420 6310
6421 - // Get post type visibility settings
6422 - $visibility_mode = isset($options['post_type_visibility_mode']) ? $options['post_type_visibility_mode'] : 'all';
6423 - $visibility_list = isset($options['post_type_visibility_list']) ? $options['post_type_visibility_list'] : array();
6424 - if (!is_array($visibility_list)) {
6425 - $visibility_list = array();
6311 + echo '<div class="' . esc_attr($class) . '">';
6312 + echo sprintf(
6313 + '<input type="text"
6314 + id="user_message_font_color"
6315 + name="user_message_font_color"
6316 + value="%s"
6317 + class="my-color-field"
6318 + data-default-color="#ffffff"
6319 + %s />',
6320 + isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
6321 + esc_attr($disabled)
6322 + );
6323 +
6324 + if (!$this->is_activated) {
6325 + echo '<div class="pro-feature-overlay">';
6326 + echo '<a href="https://mxchat.ai/" target="_blank">';
6327 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6328 + echo '</a>';
6329 + echo '</div>';
6426 6330 }
6331 + echo '</div>';
6332 +}
6427 6333
6428 - echo '<div class="mxchat-autosave-section">';
6334 +public function mxchat_bot_message_bg_color_callback() {
6335 + $disabled = $this->is_activated ? '' : 'disabled';
6336 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6429 6337
6430 - // Main toggle
6431 - echo '<label class="toggle-switch">';
6338 + echo '<div class="' . esc_attr($class) . '">';
6432 6339 echo sprintf(
6433 - '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
6434 - esc_attr($checked)
6340 + '<input type="text"
6341 + id="bot_message_bg_color"
6342 + name="bot_message_bg_color"
6343 + value="%s"
6344 + class="my-color-field"
6345 + data-default-color="#e1e1e1"
6346 + %s />',
6347 + isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
6348 + esc_attr($disabled)
6435 6349 );
6436 - echo '<span class="slider"></span>';
6437 - echo '</label>';
6438 6350
6439 - // Post Type Visibility Options (only visible when auto-display is ON)
6440 - $display_style = ($append_to_body === 'on') ? '' : 'display: none;';
6441 - echo '<div id="post-type-visibility-options" class="mxchat-sub-options" style="' . esc_attr($display_style) . '">';
6351 + if (!$this->is_activated) {
6352 + echo '<div class="pro-feature-overlay">';
6353 + echo '<a href="https://mxchat.ai/" target="_blank">';
6354 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6355 + echo '</a>';
6356 + echo '</div>';
6357 + }
6358 + echo '</div>';
6359 +}
6442 6360
6443 - echo '<div class="mxchat-post-type-visibility-header">';
6444 - echo '<h4>' . esc_html__('Post Type Visibility', 'mxchat') . '</h4>';
6361 +public function mxchat_bot_message_font_color_callback() {
6362 + $disabled = $this->is_activated ? '' : 'disabled';
6363 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6364 +
6365 + echo '<div class="' . esc_attr($class) . '">';
6366 + echo sprintf(
6367 + '<input type="text"
6368 + id="bot_message_font_color"
6369 + name="bot_message_font_color"
6370 + value="%s"
6371 + class="my-color-field"
6372 + data-default-color="#333333"
6373 + %s />',
6374 + isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
6375 + esc_attr($disabled)
6376 + );
6377 +
6378 + if (!$this->is_activated) {
6379 + echo '<div class="pro-feature-overlay">';
6380 + echo '<a href="https://mxchat.ai/" target="_blank">';
6381 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6382 + echo '</a>';
6383 + echo '</div>';
6384 + }
6445 6385 echo '</div>';
6386 +}
6446 6387
6447 - // Mode selector (radio buttons)
6448 - echo '<div class="mxchat-visibility-mode">';
6388 +public function mxchat_live_agent_message_bg_color_callback() {
6389 + $disabled = $this->is_activated ? '' : 'disabled';
6390 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6449 6391
6450 - echo '<label class="mxchat-radio-label">';
6451 - echo '<input type="radio" name="post_type_visibility_mode" value="all" ' . checked($visibility_mode, 'all', false) . ' />';
6452 - echo '<span>' . esc_html__('Show on all post types', 'mxchat') . '</span>';
6453 - echo '</label>';
6392 + echo '<div class="' . esc_attr($class) . '">';
6393 + echo sprintf(
6394 + '<input type="text"
6395 + id="live_agent_message_bg_color"
6396 + name="live_agent_message_bg_color"
6397 + value="%s"
6398 + class="my-color-field"
6399 + data-default-color="#ffffff"
6400 + %s />',
6401 + isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
6402 + esc_attr($disabled)
6403 + );
6454 6404
6455 - echo '<label class="mxchat-radio-label">';
6456 - echo '<input type="radio" name="post_type_visibility_mode" value="include" ' . checked($visibility_mode, 'include', false) . ' />';
6457 - echo '<span>' . esc_html__('Only show on selected post types', 'mxchat') . '</span>';
6458 - echo '</label>';
6405 + if (!$this->is_activated) {
6406 + echo '<div class="pro-feature-overlay">';
6407 + echo '<a href="https://mxchat.ai/" target="_blank">';
6408 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6409 + echo '</a>';
6410 + echo '</div>';
6411 + }
6412 + echo '</div>';
6413 +}
6459 6414
6460 - echo '<label class="mxchat-radio-label">';
6461 - echo '<input type="radio" name="post_type_visibility_mode" value="exclude" ' . checked($visibility_mode, 'exclude', false) . ' />';
6462 - echo '<span>' . esc_html__('Hide on selected post types', 'mxchat') . '</span>';
6463 - echo '</label>';
6415 +public function mxchat_live_agent_message_font_color_callback() {
6416 + $disabled = $this->is_activated ? '' : 'disabled';
6417 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6464 6418
6419 + echo '<div class="' . esc_attr($class) . '">';
6420 + echo sprintf(
6421 + '<input type="text"
6422 + id="live_agent_message_font_color"
6423 + name="live_agent_message_font_color"
6424 + value="%s"
6425 + class="my-color-field"
6426 + data-default-color="#333333"
6427 + %s />',
6428 + isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
6429 + esc_attr($disabled)
6430 + );
6431 +
6432 + if (!$this->is_activated) {
6433 + echo '<div class="pro-feature-overlay">';
6434 + echo '<a href="https://mxchat.ai/" target="_blank">';
6435 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6436 + echo '</a>';
6437 + echo '</div>';
6438 + }
6465 6439 echo '</div>';
6440 +}
6466 6441
6467 - // Post type checkboxes (only visible when mode is include or exclude)
6468 - $list_display = ($visibility_mode !== 'all') ? '' : 'display: none;';
6469 - echo '<div id="post-type-list" class="mxchat-post-type-list" style="' . esc_attr($list_display) . '">';
6442 +public function mxchat_mode_indicator_bg_color_callback() {
6443 + $disabled = $this->is_activated ? '' : 'disabled';
6444 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6470 6445
6471 - // Get all public post types
6472 - $post_types = get_post_types(array('public' => true), 'objects');
6446 + echo '<div class="' . esc_attr($class) . '">';
6447 + echo sprintf(
6448 + '<input type="text"
6449 + id="mode_indicator_bg_color"
6450 + name="mode_indicator_bg_color"
6451 + value="%s"
6452 + class="my-color-field"
6453 + data-default-color="#767676"
6454 + %s />',
6455 + isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
6456 + esc_attr($disabled)
6457 + );
6473 6458
6474 - foreach ($post_types as $post_type) {
6475 - // Skip attachments
6476 - if ($post_type->name === 'attachment') {
6477 - continue;
6478 - }
6459 + if (!$this->is_activated) {
6460 + echo '<div class="pro-feature-overlay">';
6461 + echo '<a href="https://mxchat.ai/" target="_blank">';
6462 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6463 + echo '</a>';
6464 + echo '</div>';
6465 + }
6466 + echo '</div>';
6467 +}
6479 6468
6480 - $is_checked = in_array($post_type->name, $visibility_list) ? 'checked' : '';
6469 +public function mxchat_mode_indicator_font_color_callback() {
6470 + $disabled = $this->is_activated ? '' : 'disabled';
6471 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6481 6472
6482 - echo '<label class="mxchat-checkbox-label">';
6483 - echo '<input type="checkbox" name="post_type_visibility_list[]" value="' . esc_attr($post_type->name) . '" ' . $is_checked . ' />';
6484 - echo '<span>' . esc_html($post_type->label) . '</span>';
6485 - echo '</label>';
6473 + echo '<div class="' . esc_attr($class) . '">';
6474 + echo sprintf(
6475 + '<input type="text"
6476 + id="mode_indicator_font_color"
6477 + name="mode_indicator_font_color"
6478 + value="%s"
6479 + class="my-color-field"
6480 + data-default-color="#ffffff"
6481 + %s />',
6482 + isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
6483 + esc_attr($disabled)
6484 + );
6485 +
6486 + if (!$this->is_activated) {
6487 + echo '<div class="pro-feature-overlay">';
6488 + echo '<a href="https://mxchat.ai/" target="_blank">';
6489 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6490 + echo '</a>';
6491 + echo '</div>';
6486 6492 }
6493 + echo '</div>';
6494 +}
6487 6495
6488 - echo '</div>'; // End post-type-list
6489 - echo '</div>'; // End post-type-visibility-options
6490 - echo '</div>'; // End mxchat-autosave-section
6496 +public function mxchat_toolbar_icon_color_callback() {
6497 + $disabled = $this->is_activated ? '' : 'disabled';
6498 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6499 +
6500 + echo '<div class="' . esc_attr($class) . '">';
6501 + echo sprintf(
6502 + '<input type="text"
6503 + id="toolbar_icon_color"
6504 + name="toolbar_icon_color"
6505 + value="%s"
6506 + class="my-color-field"
6507 + data-default-color="#212121"
6508 + %s />',
6509 + isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
6510 + esc_attr($disabled)
6511 + );
6512 +
6513 + if (!$this->is_activated) {
6514 + echo '<div class="pro-feature-overlay">';
6515 + echo '<a href="https://mxchat.ai/" target="_blank">';
6516 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6517 + echo '</a>';
6518 + echo '</div>';
6519 + }
6520 + echo '</div>';
6491 6521 }
6492 6522
6493 -public function mxchat_contextual_awareness_callback() {
6523 +public function mxchat_top_bar_bg_color_callback() {
6524 + $disabled = $this->is_activated ? '' : 'disabled';
6525 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6526 +
6527 + echo '<div class="' . esc_attr($class) . '">';
6528 + echo sprintf(
6529 + '<input type="text"
6530 + id="top_bar_bg_color"
6531 + name="top_bar_bg_color"
6532 + value="%s"
6533 + class="my-color-field"
6534 + data-default-color="#00b294"
6535 + %s />',
6536 + isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
6537 + esc_attr($disabled)
6538 + );
6539 +
6540 + if (!$this->is_activated) {
6541 + echo '<div class="pro-feature-overlay">';
6542 + echo '<a href="https://mxchat.ai/" target="_blank">';
6543 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6544 + echo '</a>';
6545 + echo '</div>';
6546 + }
6547 + echo '</div>';
6548 +}
6549 +
6550 +public function mxchat_send_button_font_color_callback() {
6551 + $disabled = $this->is_activated ? '' : 'disabled';
6552 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6553 +
6554 + echo '<div class="' . esc_attr($class) . '">';
6555 + echo sprintf(
6556 + '<input type="text"
6557 + id="send_button_font_color"
6558 + name="send_button_font_color"
6559 + value="%s"
6560 + class="my-color-field"
6561 + data-default-color="#ffffff"
6562 + %s />',
6563 + isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
6564 + esc_attr($disabled)
6565 + );
6566 +
6567 + if (!$this->is_activated) {
6568 + echo '<div class="pro-feature-overlay">';
6569 + echo '<a href="https://mxchat.ai/" target="_blank">';
6570 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6571 + echo '</a>';
6572 + echo '</div>';
6573 + }
6574 + echo '</div>';
6575 +}
6576 +
6577 +public function mxchat_chatbot_background_color_callback() {
6578 + $disabled = $this->is_activated ? '' : 'disabled';
6579 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6580 +
6581 + echo '<div class="' . esc_attr($class) . '">';
6582 + echo sprintf(
6583 + '<input type="text"
6584 + id="chatbot_background_color"
6585 + name="chatbot_background_color"
6586 + value="%s"
6587 + class="my-color-field"
6588 + data-default-color="#000000"
6589 + %s />',
6590 + isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
6591 + esc_attr($disabled)
6592 + );
6593 +
6594 + if (!$this->is_activated) {
6595 + echo '<div class="pro-feature-overlay">';
6596 + echo '<a href="https://mxchat.ai/" target="_blank">';
6597 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6598 + echo '</a>';
6599 + echo '</div>';
6600 + }
6601 + echo '</div>';
6602 +}
6603 +
6604 +public function mxchat_icon_color_callback() {
6605 + $disabled = $this->is_activated ? '' : 'disabled';
6606 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6607 +
6608 + echo '<div class="' . esc_attr($class) . '">';
6609 + echo sprintf(
6610 + '<input type="text"
6611 + id="icon_color"
6612 + name="icon_color"
6613 + value="%s"
6614 + class="my-color-field"
6615 + data-default-color="#ffffff"
6616 + %s />',
6617 + isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
6618 + esc_attr($disabled)
6619 + );
6620 +
6621 + if (!$this->is_activated) {
6622 + echo '<div class="pro-feature-overlay">';
6623 + echo '<a href="https://mxchat.ai/" target="_blank">';
6624 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6625 + echo '</a>';
6626 + echo '</div>';
6627 + }
6628 + echo '</div>';
6629 +}
6630 +
6631 +public function mxchat_custom_icon_callback() {
6632 + $disabled = $this->is_activated ? '' : 'disabled';
6633 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6634 + $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
6635 +
6636 + echo '<div class="' . esc_attr($class) . '">';
6637 + echo sprintf(
6638 + '<input type="url"
6639 + id="custom_icon"
6640 + name="custom_icon"
6641 + value="%s"
6642 + placeholder="%s"
6643 + class="regular-text"
6644 + %s />',
6645 + $custom_icon_url,
6646 + esc_attr__('Enter PNG URL', 'mxchat'),
6647 + esc_attr($disabled)
6648 + );
6649 +
6650 + // Preview container for the icon
6651 + if (!empty($custom_icon_url)) {
6652 + echo '<div class="icon-preview" style="margin-top: 10px;">';
6653 + echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
6654 + echo '</div>';
6655 + }
6656 +
6657 + echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
6658 +
6659 + if (!$this->is_activated) {
6660 + echo '<div class="pro-feature-overlay">';
6661 + echo '<a href="https://mxchat.ai/" target="_blank">';
6662 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6663 + echo '</a>';
6664 + echo '</div>';
6665 + }
6666 + echo '</div>';
6667 +}
6668 +
6669 +public function mxchat_title_icon_callback() {
6670 + $disabled = $this->is_activated ? '' : 'disabled';
6671 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6672 + // Fixed the variable reference - it was using custom_icon instead of title_icon
6673 + $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
6674 +
6675 + echo '<div class="' . esc_attr($class) . '">';
6676 + echo sprintf(
6677 + '<input type="url"
6678 + id="title_icon"
6679 + name="title_icon"
6680 + value="%s"
6681 + placeholder="%s"
6682 + class="regular-text"
6683 + %s />',
6684 + $title_icon_url,
6685 + esc_attr__('Enter PNG URL', 'mxchat'),
6686 + esc_attr($disabled)
6687 + );
6688 +
6689 + // Preview container for the icon
6690 + if (!empty($title_icon_url)) {
6691 + echo '<div class="icon-preview" style="margin-top: 10px;">';
6692 + echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
6693 + echo '</div>';
6694 + }
6695 +
6696 + echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
6697 +
6698 + if (!$this->is_activated) {
6699 + echo '<div class="pro-feature-overlay">';
6700 + echo '<a href="https://mxchat.ai/" target="_blank">';
6701 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6702 + echo '</a>';
6703 + echo '</div>';
6704 + }
6705 + echo '</div>';
6706 +}
6707 +
6708 +public function mxchat_chat_input_font_color_callback() {
6709 + $disabled = $this->is_activated ? '' : 'disabled';
6710 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6711 +
6712 + echo '<div class="' . esc_attr($class) . '">';
6713 + echo sprintf(
6714 + '<input type="text"
6715 + id="chat_input_font_color"
6716 + name="chat_input_font_color"
6717 + value="%s"
6718 + class="my-color-field"
6719 + data-default-color="#555555"
6720 + %s />',
6721 + isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
6722 + esc_attr($disabled)
6723 + );
6724 +
6725 + if (!$this->is_activated) {
6726 + echo '<div class="pro-feature-overlay">';
6727 + echo '<a href="https://mxchat.ai/" target="_blank">';
6728 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6729 + echo '</a>';
6730 + echo '</div>';
6731 + }
6732 + echo '</div>';
6733 +}
6734 +
6735 +public function mxchat_append_to_body_callback() {
6494 6736 // Get value from options array, default to 'off'
6495 - $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
6496 - $checked = ($contextual_awareness === 'on') ? 'checked' : '';
6737 + $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
6738 + $checked = ($append_to_body === 'on') ? 'checked' : '';
6739 +
6497 6740 echo '<label class="toggle-switch">';
6498 6741 echo sprintf(
6499 - '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
6742 + '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
6500 6743 esc_attr($checked)
6501 6744 );
6502 6745 echo '<span class="slider"></span>';
6503 6746 echo '</label>';
6747 + echo '<p class="description">' .
6748 + esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
6749 + '</p>';
6750 +
6504 6751 }
6505 6752
6506 -public function mxchat_citation_links_toggle_callback() {
6507 - // Get value from options array, default to 'on' (enabled by default)
6508 - $citation_links = isset($this->options['citation_links_toggle']) ? $this->options['citation_links_toggle'] : 'on';
6509 - $checked = ($citation_links === 'on') ? 'checked' : '';
6753 +public function mxchat_contextual_awareness_callback() {
6754 + // Get value from options array, default to 'off'
6755 + $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
6756 + $checked = ($contextual_awareness === 'on') ? 'checked' : '';
6510 6757 echo '<label class="toggle-switch">';
6511 6758 echo sprintf(
6512 - '<input type="checkbox" id="citation_links_toggle" name="citation_links_toggle" value="on" %s />',
6759 + '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
6513 6760 esc_attr($checked)
6514 6761 );
6515 6762 echo '<span class="slider"></span>';
6516 6763 echo '</label>';
6764 + echo '<p class="description">' .
6765 + esc_html__('Enable contextual awareness to allow the chatbot to understand and reference the current page content. When enabled, the chatbot will have access to the page title, content, and URL for more relevant responses.', 'mxchat') .
6766 + '</p>';
6517 6767 }
6518 6768
6769 +
6519 6770 public function mxchat_privacy_toggle_callback() {
6520 6771 // Load from mxchat_options array
6521 6772 $options = get_option('mxchat_options', []);
6522 6773
@@ -6536,8 +6787,9 @@
6536 6787 esc_attr($checked)
6537 6788 );
6538 6789 echo '<span class="slider"></span>';
6539 6790 echo '</label>';
6791 + echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
6540 6792
6541 6793 // Output the custom text input field
6542 6794 echo sprintf(
6543 6795 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
@@ -6542,8 +6794,9 @@
6542 6794 echo sprintf(
6543 6795 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
6544 6796 esc_textarea($privacy_text)
6545 6797 );
6798 + echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
6546 6799 }
6547 6800
6548 6801
6549 6802 public function mxchat_complianz_toggle_callback() {
@@ -6561,8 +6814,10 @@
6561 6814 esc_attr($checked)
6562 6815 );
6563 6816 echo '<span class="slider"></span>';
6564 6817 echo '</label>';
6818 +
6819 + echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
6565 6820 }
6566 6821
6567 6822 public function mxchat_link_target_toggle_callback() {
6568 6823 // Load from mxchat_options array
@@ -6579,8 +6834,9 @@
6579 6834 esc_attr($checked)
6580 6835 );
6581 6836 echo '<span class="slider"></span>';
6582 6837 echo '</label>';
6838 + echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
6583 6839 }
6584 6840
6585 6841 public function mxchat_chat_persistence_toggle_callback() {
6586 6842 // Load from mxchat_options array
@@ -6597,8 +6853,10 @@
6597 6853 esc_attr($checked)
6598 6854 );
6599 6855 echo '<span class="slider"></span>';
6600 6856 echo '</label>';
6857 +
6858 + 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>';
6601 6859 }
6602 6860
6603 6861 public function mxchat_popular_question_1_callback() {
6604 6862 // Load the full plugin options array
@@ -6612,8 +6870,11 @@
6612 6870 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
6613 6871 esc_attr($popular_question_1),
6614 6872 esc_attr__('Enter Quick Question 1', 'mxchat')
6615 6873 );
6874 +
6875 + // Add a description for the field
6876 + echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
6616 6877 }
6617 6878
6618 6879
6619 6880 public function mxchat_popular_question_2_callback() {
@@ -6628,8 +6889,11 @@
6628 6889 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
6629 6890 esc_attr($popular_question_2),
6630 6891 esc_attr__('Enter Quick Question 2', 'mxchat')
6631 6892 );
6893 +
6894 + // Add a description for the field
6895 + echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
6632 6896 }
6633 6897
6634 6898
6635 6899 public function mxchat_popular_question_3_callback() {
@@ -6644,8 +6908,11 @@
6644 6908 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
6645 6909 esc_attr($popular_question_3),
6646 6910 esc_attr(__('Enter Quick Question 3', 'mxchat'))
6647 6911 );
6912 +
6913 + // Add a description for the field
6914 + echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
6648 6915 }
6649 6916
6650 6917 public function mxchat_additional_popular_questions_callback() {
6651 6918 $options = get_option('mxchat_options', []);
@@ -6694,19 +6961,19 @@
6694 6961 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
6695 6962 esc_attr(__('Add question', 'mxchat')),
6696 6963 esc_html__('Add Question', 'mxchat')
6697 6964 );
6965 + echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
6966 + echo '</div>';
6698 6967 }
6699 6968
6700 6969 public function mxchat_brave_api_key_callback() {
6701 6970 $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
6702 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6703 6971
6704 6972 echo '<div class="api-key-wrapper">';
6705 6973 echo sprintf(
6706 - '<input type="text" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text mxchat-autosave-field mxchat-api-key-field" autocomplete="new-password" data-lpignore="true" data-form-type="other" data-nonce="%s" />',
6707 - $brave_api_key,
6708 - $nonce
6974 + '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
6975 + $brave_api_key
6709 6976 );
6710 6977 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6711 6978 echo '</div>';
6712 6979 echo '<p class="description">' . __('Required for Brave Search integration. Get your API key from Brave Search API.', 'mxchat') . '</p>';
@@ -6715,18 +6982,14 @@
6715 6982 public function mxchat_brave_image_count_callback() {
6716 6983 $brave_image_count = isset($this->options['brave_image_count'])
6717 6984 ? intval($this->options['brave_image_count'])
6718 6985 : 4;
6719 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6720 6986
6721 - echo '<div class="mxchat-field-wrapper">';
6722 6987 echo sprintf(
6723 6988 '<input type="number" id="brave_image_count" name="brave_image_count"
6724 - value="%d" min="1" max="6" class="small-text mxchat-autosave-field" data-nonce="%s" />',
6725 - $brave_image_count,
6726 - $nonce
6989 + value="%d" min="1" max="6" class="small-text" />',
6990 + $brave_image_count
6727 6991 );
6728 - echo '</div>';
6729 6992 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
6730 6993 }
6731 6994
6732 6995 public function mxchat_brave_safe_search_callback() {
@@ -6732,12 +6995,10 @@
6732 6995 public function mxchat_brave_safe_search_callback() {
6733 6996 $brave_safe_search = isset($this->options['brave_safe_search'])
6734 6997 ? esc_attr($this->options['brave_safe_search'])
6735 6998 : 'strict';
6736 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6737 6999
6738 - echo '<div class="mxchat-field-wrapper">';
6739 - echo '<select id="brave_safe_search" name="brave_safe_search" class="mxchat-autosave-field" data-nonce="' . $nonce . '">';
7000 + echo '<select id="brave_safe_search" name="brave_safe_search">';
6740 7001 echo sprintf(
6741 7002 '<option value="strict" %s>%s</option>',
6742 7003 selected($brave_safe_search, 'strict', false),
6743 7004 __('Strict', 'mxchat')
@@ -6747,9 +7008,8 @@
6747 7008 selected($brave_safe_search, 'off', false),
6748 7009 __('Off', 'mxchat')
6749 7010 );
6750 7011 echo '</select>';
6751 - echo '</div>';
6752 7012 echo '<p class="description">' .
6753 7013 esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
6754 7014 '</p>';
6755 7015 }
@@ -6757,18 +7017,14 @@
6757 7017 public function mxchat_brave_news_count_callback() {
6758 7018 $brave_news_count = isset($this->options['brave_news_count'])
6759 7019 ? intval($this->options['brave_news_count'])
6760 7020 : 3;
6761 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6762 7021
6763 - echo '<div class="mxchat-field-wrapper">';
6764 7022 echo sprintf(
6765 7023 '<input type="number" id="brave_news_count" name="brave_news_count"
6766 - value="%d" min="1" max="10" class="small-text mxchat-autosave-field" data-nonce="%s" />',
6767 - $brave_news_count,
6768 - $nonce
7024 + value="%d" min="1" max="10" class="small-text" />',
7025 + $brave_news_count
6769 7026 );
6770 - echo '</div>';
6771 7027 echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
6772 7028 }
6773 7029
6774 7030 public function mxchat_brave_country_callback() {
@@ -6774,18 +7030,14 @@
6774 7030 public function mxchat_brave_country_callback() {
6775 7031 $brave_country = isset($this->options['brave_country'])
6776 7032 ? esc_attr($this->options['brave_country'])
6777 7033 : 'us';
6778 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6779 7034
6780 - echo '<div class="mxchat-field-wrapper">';
6781 7035 echo sprintf(
6782 7036 '<input type="text" id="brave_country" name="brave_country"
6783 - value="%s" maxlength="2" class="small-text mxchat-autosave-field" data-nonce="%s" />',
6784 - $brave_country,
6785 - $nonce
7037 + value="%s" maxlength="2" class="small-text" />',
7038 + $brave_country
6786 7039 );
6787 - echo '</div>';
6788 7040 echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
6789 7041 }
6790 7042
6791 7043 public function mxchat_brave_language_callback() {
@@ -6791,18 +7043,14 @@
6791 7043 public function mxchat_brave_language_callback() {
6792 7044 $brave_language = isset($this->options['brave_language'])
6793 7045 ? esc_attr($this->options['brave_language'])
6794 7046 : 'en';
6795 - $nonce = wp_create_nonce('mxchat_autosave_nonce');
6796 7047
6797 - echo '<div class="mxchat-field-wrapper">';
6798 7048 echo sprintf(
6799 7049 '<input type="text" id="brave_language" name="brave_language"
6800 - value="%s" maxlength="2" class="small-text mxchat-autosave-field" data-nonce="%s" />',
6801 - $brave_language,
6802 - $nonce
7050 + value="%s" maxlength="2" class="small-text" />',
7051 + $brave_language
6803 7052 );
6804 - echo '</div>';
6805 7053 echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
6806 7054 }
6807 7055
6808 7056
@@ -7022,97 +7270,13 @@
7022 7270 );
7023 7271 echo '<p class="description">' . esc_html__('Enter Slack User IDs of agents who should be automatically invited to chat channels (one per line). Find user IDs in Slack profiles under "More" → "Copy member ID". Example: U1234567890', 'mxchat') . '</p>';
7024 7272 }
7025 7273
7026 -/**
7027 - * Telegram Integration Callbacks
7028 - */
7029 -public function mxchat_telegram_section_callback() {
7030 - echo '<p>' . esc_html__('Configure Telegram integration for live agent support.', 'mxchat') . '</p>';
7031 -}
7032 -
7033 -public function mxchat_telegram_status_callback() {
7034 - $fresh_options = get_option('mxchat_options');
7035 - $status = isset($fresh_options['telegram_status']) ? $fresh_options['telegram_status'] : 'off';
7036 -
7037 - echo '<label class="toggle-switch">';
7038 - echo sprintf(
7039 - '<input type="checkbox" id="telegram_status" name="telegram_status" value="on" %s />',
7040 - checked($status, 'on', false)
7041 - );
7042 - echo '<span class="slider"></span>';
7043 - echo '</label>';
7044 - echo '<label for="telegram_status" class="mxchat-status-label">';
7045 - echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
7046 - echo '</label>';
7047 -}
7048 -
7049 -public function mxchat_telegram_notification_message_callback() {
7050 - $message = isset($this->options['telegram_notification_message'])
7051 - ? $this->options['telegram_notification_message']
7052 - : __("I've notified a support agent. Please allow a moment for them to respond.", 'mxchat');
7053 -
7054 - printf(
7055 - '<textarea id="telegram_notification_message" name="telegram_notification_message" rows="3" cols="50">%s</textarea>',
7056 - esc_textarea($message)
7057 - );
7058 - echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
7059 -}
7060 -
7061 -public function mxchat_telegram_away_message_callback() {
7062 - $message = isset($this->options['telegram_away_message'])
7063 - ? $this->options['telegram_away_message']
7064 - : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
7065 -
7066 - printf(
7067 - '<textarea id="telegram_away_message" name="telegram_away_message" rows="3" cols="50">%s</textarea>',
7068 - esc_textarea($message)
7069 - );
7070 - echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
7071 -}
7072 -
7073 -public function mxchat_telegram_bot_token_callback() {
7074 - printf(
7075 - '<input type="password" id="telegram_bot_token" name="telegram_bot_token" value="%s" class="regular-text" />',
7076 - isset($this->options['telegram_bot_token']) ? esc_attr($this->options['telegram_bot_token']) : ''
7077 - );
7078 - echo '<button type="button" class="button button-secondary" onclick="var f=document.getElementById(\'telegram_bot_token\'); if(f.type===\'password\'){f.type=\'text\';this.textContent=\'' . esc_js(__('Hide', 'mxchat')) . '\';}else{f.type=\'password\';this.textContent=\'' . esc_js(__('Show', 'mxchat')) . '\';}">' . esc_html__('Show', 'mxchat') . '</button>';
7079 - echo '<p class="description">' . esc_html__('Your Telegram Bot Token from @BotFather (e.g., 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ).', 'mxchat') . '</p>';
7080 -}
7081 -
7082 -public function mxchat_telegram_group_id_callback() {
7083 - printf(
7084 - '<input type="text" id="telegram_group_id" name="telegram_group_id" value="%s" class="regular-text" />',
7085 - isset($this->options['telegram_group_id']) ? esc_attr($this->options['telegram_group_id']) : ''
7086 - );
7087 - echo '<p class="description">' . esc_html__('Your Telegram supergroup ID (starts with -100). The group must have forum topics enabled.', 'mxchat') . '</p>';
7088 -}
7089 -
7090 -public function mxchat_telegram_webhook_secret_callback() {
7091 - $secret = isset($this->options['telegram_webhook_secret']) ? $this->options['telegram_webhook_secret'] : '';
7092 -
7093 - // Auto-generate secret if empty
7094 - if (empty($secret)) {
7095 - $secret = wp_generate_password(64, false, false);
7096 - $options = get_option('mxchat_options', []);
7097 - $options['telegram_webhook_secret'] = $secret;
7098 - update_option('mxchat_options', $options);
7099 - $this->options['telegram_webhook_secret'] = $secret;
7100 - }
7101 -
7102 - printf(
7103 - '<input type="password" id="telegram_webhook_secret" name="telegram_webhook_secret" value="%s" class="regular-text" />',
7104 - esc_attr($secret)
7105 - );
7106 - echo '<button type="button" class="button button-secondary" onclick="var f=document.getElementById(\'telegram_webhook_secret\'); if(f.type===\'password\'){f.type=\'text\';this.textContent=\'' . esc_js(__('Hide', 'mxchat')) . '\';}else{f.type=\'password\';this.textContent=\'' . esc_js(__('Show', 'mxchat')) . '\';}">' . esc_html__('Show', 'mxchat') . '</button>';
7107 - echo '<p class="description">' . esc_html__('Secret token for webhook verification. Use this when setting up your Telegram webhook with the secret_token parameter.', 'mxchat') . '</p>';
7108 -}
7109 -
7110 7274 public function mxchat_similarity_threshold_callback() {
7111 7275 // Load from mxchat_options array
7112 7276 $options = get_option('mxchat_options', []);
7113 7277
7114 - // Get value from options array with default of 35
7278 + // Get value from options array with default of 80
7115 7279 $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
7116 7280
7117 7281 echo '<div class="slider-container">';
7118 7282 echo sprintf(
@@ -7130,62 +7294,13 @@
7130 7294 '<span id="threshold_value" class="range-value">%s</span>',
7131 7295 esc_html($threshold)
7132 7296 );
7133 7297 echo '</div>';
7298 + echo '<p class="description">';
7299 + echo esc_html__('Adjust similarity threshold for optimal content matching. Too high may limit knowledge retrieval. We highly recommend using the MxChat Debugger found on the left side of your website when logged in as admin while testing the bot.', 'mxchat');
7300 + echo '</p>';
7134 7301 }
7135 7302
7136 -public function mxchat_rag_sources_limit_callback() {
7137 - // Load from mxchat_options array
7138 - $options = get_option('mxchat_options', []);
7139 -
7140 - // Get value from options array with default of 3
7141 - $rag_sources_limit = isset($options['rag_sources_limit']) ? intval($options['rag_sources_limit']) : 3;
7142 -
7143 - echo '<div class="slider-container">';
7144 - echo sprintf(
7145 - '<input type="range"
7146 - id="rag_sources_limit"
7147 - name="rag_sources_limit"
7148 - min="3"
7149 - max="10"
7150 - step="1"
7151 - value="%s"
7152 - class="range-slider" />',
7153 - esc_attr($rag_sources_limit)
7154 - );
7155 - echo sprintf(
7156 - '<span id="rag_sources_limit_value" class="range-value">%s</span>',
7157 - esc_html($rag_sources_limit)
7158 - );
7159 - echo '</div>';
7160 -}
7161 -
7162 -public function mxchat_rag_chunks_limit_callback() {
7163 - // Load from mxchat_options array
7164 - $options = get_option('mxchat_options', []);
7165 -
7166 - // Get value from options array with default of 15
7167 - $rag_chunks_limit = isset($options['rag_chunks_limit']) ? intval($options['rag_chunks_limit']) : 15;
7168 -
7169 - echo '<div class="slider-container">';
7170 - echo sprintf(
7171 - '<input type="range"
7172 - id="rag_chunks_limit"
7173 - name="rag_chunks_limit"
7174 - min="8"
7175 - max="20"
7176 - step="1"
7177 - value="%s"
7178 - class="range-slider" />',
7179 - esc_attr($rag_chunks_limit)
7180 - );
7181 - echo sprintf(
7182 - '<span id="rag_chunks_limit_value" class="range-value">%s</span>',
7183 - esc_html($rag_chunks_limit)
7184 - );
7185 - echo '</div>';
7186 -}
7187 -
7188 7303 public function mxchat_enqueue_admin_assets() {
7189 7304 // Get plugin version
7190 7305 $version = MXCHAT_VERSION;
7191 7306
@@ -7211,11 +7326,8 @@
7211 7326 // Core admin styles
7212 7327 wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
7213 7328 wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
7214 7329
7215 - // New sidebar navigation styles (for main settings page)
7216 - wp_enqueue_style('mxchat-admin-sidebar-css', $plugin_url . 'css/admin-sidebar.css', array(), $version);
7217 -
7218 7330 // Core admin scripts
7219 7331 wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
7220 7332 }
7221 7333 private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
@@ -7223,72 +7335,24 @@
7223 7335 case 'mxchat-prompts':
7224 7336 // Knowledge processing page assets
7225 7337 wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
7226 7338 wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
7227 - // Add the knowledge processing script (common script needed for WordPress dismiss functionality)
7228 - wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery', 'common'), $version, true);
7339 + // Add the knowledge processing script
7340 + wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
7229 7341 break;
7230 7342
7231 7343 case 'mxchat-transcripts':
7232 - // Load admin sidebar CSS first (shared styles for sidebar navigation)
7233 - wp_enqueue_style('mxchat-admin-sidebar-css', $plugin_url . 'css/admin-sidebar.css', array(), $version);
7234 - // Load transcripts-specific styles
7235 - wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array('mxchat-admin-sidebar-css'), $version);
7344 + wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
7236 7345 wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
7237 7346 break;
7238 7347
7239 7348 case 'mxchat-actions':
7240 - // Load admin sidebar CSS first (shared styles for sidebar navigation)
7241 - wp_enqueue_style('mxchat-admin-sidebar-css', $plugin_url . 'css/admin-sidebar.css', array(), $version);
7242 - // Load actions-specific styles
7243 - wp_enqueue_style('mxchat-actions-css', $plugin_url . 'css/actions.css', array('mxchat-admin-sidebar-css'), $version);
7244 - wp_enqueue_script('mxchat-actions-js', $plugin_url . 'js/mxchat_actions.js', array('jquery'), $version, true);
7245 -
7246 - // Localize script data for actions page
7247 - $is_activated = get_option('mxchat_pro_license_status') === 'active';
7248 - wp_localize_script('mxchat-actions-js', 'mxchActionsData', array(
7249 - 'ajaxUrl' => admin_url('admin-ajax.php'),
7250 - 'nonce' => wp_create_nonce('mxchat_actions_nonce'),
7251 - 'addNonce' => wp_create_nonce('mxchat_add_intent_nonce'),
7252 - 'editNonce' => wp_create_nonce('mxchat_edit_intent'),
7253 - 'deleteNonce' => wp_create_nonce('mxchat_delete_intent_nonce'),
7254 - 'toggleNonce' => wp_create_nonce('mxchat_actions_nonce'),
7255 - 'addPhraseNonce' => wp_create_nonce('mxchat_add_phrase_nonce'),
7256 - 'deletePhraseNonce' => wp_create_nonce('mxchat_delete_phrase_nonce'),
7257 - 'getPhrasesNonce' => wp_create_nonce('mxchat_get_phrases_nonce'),
7258 - 'deleteLegacyNonce' => wp_create_nonce('mxchat_delete_legacy_nonce'),
7259 - 'isActivated' => $is_activated,
7260 - 'i18n' => array(
7261 - 'confirmDelete' => __('Are you sure you want to delete this action?', 'mxchat'),
7262 - 'confirmBulkDelete' => __('Are you sure you want to delete the selected actions?', 'mxchat'),
7263 - 'saving' => __('Saving...', 'mxchat'),
7264 - 'saved' => __('Saved successfully!', 'mxchat'),
7265 - 'error' => __('An error occurred. Please try again.', 'mxchat'),
7266 - 'proRequired' => __('This feature requires MxChat Pro.', 'mxchat'),
7267 - 'addonRequired' => __('This feature requires an add-on.', 'mxchat')
7268 - )
7269 - ));
7349 + wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
7270 7350 break;
7271 7351
7272 7352 case 'mxchat-activation':
7273 - // Load admin sidebar CSS first (shared styles for sidebar navigation)
7274 - wp_enqueue_style('mxchat-admin-sidebar-css', $plugin_url . 'css/admin-sidebar.css', array(), $version);
7275 - // Load pro page-specific styles
7276 - wp_enqueue_style('mxchat-pro-css', $plugin_url . 'css/admin-pro.css', array('mxchat-admin-sidebar-css'), $version);
7277 - // Load pro page JavaScript
7278 - wp_enqueue_script('mxchat-pro-js', $plugin_url . 'js/mxchat_pro.js', array('jquery'), $version, true);
7279 - // Load activation script for license activation/deactivation
7280 7353 wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
7281 7354 break;
7282 -
7283 - case 'mxchat-content':
7284 - // Load admin sidebar CSS (shared styles for sidebar navigation)
7285 - wp_enqueue_style('mxchat-admin-sidebar-css', $plugin_url . 'css/admin-sidebar.css', array(), $version);
7286 - // Load content page-specific styles
7287 - wp_enqueue_style('mxchat-content-css', $plugin_url . 'css/admin-content.css', array('mxchat-admin-sidebar-css'), $version);
7288 - // Load content page JavaScript
7289 - wp_enqueue_script('mxchat-content-js', $plugin_url . 'js/mxchat-content.js', array('jquery'), $version, true);
7290 - break;
7291 7355 default:
7292 7356 wp_enqueue_script(
7293 7357 'mxchat-test-streaming-js',
7294 7358 $plugin_url . 'js/mxchat-test-streaming.js',
@@ -7298,80 +7362,10 @@
7298 7362 );
7299 7363
7300 7364 wp_localize_script('mxchat-test-streaming-js', 'mxchatTestStreamingAjax', [
7301 7365 'ajax_url' => admin_url('admin-ajax.php'),
7302 - 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce'),
7303 - 'settings_nonce' => wp_create_nonce('mxchat_save_setting_nonce')
7366 + 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce')
7304 7367 ]);
7305 -
7306 - // Testing Tab: Load chatbot assets for the embedded testing chatbot
7307 - wp_enqueue_style('mxchat-chat-css', $plugin_url . 'css/chat-style.css', array(), $version);
7308 - wp_enqueue_style('mxchat-admin-testing-css', $plugin_url . 'css/admin-testing-tab.css', array('mxchat-admin-sidebar-css', 'mxchat-chat-css'), $version);
7309 -
7310 - wp_enqueue_script('mxchat-chat-js', $plugin_url . 'js/chat-script.js', array('jquery'), $version, true);
7311 - wp_enqueue_script('mxchat-admin-testing-js', $plugin_url . 'js/admin-testing-tab.js', array('jquery', 'mxchat-chat-js'), $version, true);
7312 -
7313 - // Allow add-ons to enqueue their public CSS/JS for the testing chatbot
7314 - do_action('mxchat_enqueue_testing_tab_assets');
7315 -
7316 - // Localize mxchatChat for the chatbot JS (same settings as frontend)
7317 - $options = get_option('mxchat_options', array());
7318 - $prompts_options = get_option('mxchat_prompts_options', array());
7319 - $theme_options = get_option('mxchat_theme_options', array());
7320 - $ai_theme_active = !empty($theme_options['active_ai_theme_css']);
7321 - $has_bot_theme_assignments = !empty($theme_options['bot_theme_assignments']);
7322 - $skip_inline_colors = $ai_theme_active || $has_bot_theme_assignments;
7323 -
7324 - wp_localize_script('mxchat-chat-js', 'mxchatChat', array(
7325 - 'ajax_url' => admin_url('admin-ajax.php'),
7326 - 'nonce' => wp_create_nonce('mxchat_chat_nonce'),
7327 - 'model' => isset($options['model']) ? $options['model'] : 'gpt-5.1-chat-latest',
7328 - 'enable_streaming_toggle' => isset($options['enable_streaming_toggle']) ? $options['enable_streaming_toggle'] : 'on',
7329 - 'contextual_awareness_toggle' => isset($options['contextual_awareness_toggle']) ? $options['contextual_awareness_toggle'] : 'off',
7330 - 'link_target_toggle' => $options['link_target_toggle'] ?? 'off',
7331 - 'rate_limit_message' => $options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
7332 - 'complianz_toggle' => isset($options['complianz_toggle']) && $options['complianz_toggle'] === 'on',
7333 - 'user_message_bg_color' => $options['user_message_bg_color'] ?? '#fff',
7334 - 'user_message_font_color' => $options['user_message_font_color'] ?? '#212121',
7335 - 'bot_message_bg_color' => $options['bot_message_bg_color'] ?? '#212121',
7336 - 'bot_message_font_color' => $options['bot_message_font_color'] ?? '#fff',
7337 - 'top_bar_bg_color' => $options['top_bar_bg_color'] ?? '#212121',
7338 - 'send_button_font_color' => $options['send_button_font_color'] ?? '#212121',
7339 - 'close_button_color' => $options['close_button_color'] ?? '#fff',
7340 - 'chatbot_background_color' => $options['chatbot_background_color'] ?? '#212121',
7341 - 'chatbot_bg_color' => $options['chatbot_bg_color'] ?? '#fff',
7342 - 'icon_color' => $options['icon_color'] ?? '#fff',
7343 - 'chat_input_font_color' => $options['chat_input_font_color'] ?? '#212121',
7344 - 'chat_persistence_toggle' => 'off', // Always off for testing chatbot
7345 - 'appendWidgetToBody' => 'off',
7346 - 'live_agent_message_bg_color' => $options['live_agent_message_bg_color'] ?? '#ffffff',
7347 - 'live_agent_message_font_color' => $options['live_agent_message_font_color'] ?? '#333333',
7348 - 'chat_toolbar_toggle' => $options['chat_toolbar_toggle'] ?? 'off',
7349 - 'mode_indicator_bg_color' => $options['mode_indicator_bg_color'] ?? '#767676',
7350 - 'mode_indicator_font_color' => $options['mode_indicator_font_color'] ?? '#ffffff',
7351 - 'toolbar_icon_color' => $options['toolbar_icon_color'] ?? '#212121',
7352 - 'use_pinecone' => $prompts_options['mxchat_use_pinecone'] ?? '0',
7353 - 'email_collection_enabled' => 'off', // No email collection in testing
7354 - 'initial_email_state' => null,
7355 - 'skip_email_check' => true,
7356 - 'pinecone_enabled' => isset($prompts_options['mxchat_use_pinecone']) && $prompts_options['mxchat_use_pinecone'] === '1',
7357 - 'skip_inline_colors' => $skip_inline_colors,
7358 - 'bot_theme_assignments' => $theme_options['bot_theme_assignments'] ?? array()
7359 - ));
7360 -
7361 - // Localize testing tab script data
7362 - wp_localize_script('mxchat-admin-testing-js', 'mxchatAdminTestData', array(
7363 - 'ajaxUrl' => admin_url('admin-ajax.php'),
7364 - 'nonce' => wp_create_nonce('mxchat_test_nonce'),
7365 - 'isAdmin' => true,
7366 - 'testingEnabled' => true
7367 - ));
7368 -
7369 - // Add testing enabled flag for the chatbot to return debug data
7370 - add_action('admin_footer', function() {
7371 - echo '<script>window.mxchatTestingEnabled = true;</script>';
7372 - });
7373 -
7374 7368 break;
7375 7369 }
7376 7370 }
7377 7371 private function localize_admin_scripts($current_page) {
@@ -7377,9 +7371,9 @@
7377 7371 private function localize_admin_scripts($current_page) {
7378 7372 // Base localization data for main admin script
7379 7373 $base_data = array(
7380 7374 'ajax_url' => admin_url('admin-ajax.php'),
7381 - 'nonce' => wp_create_nonce('mxchat_admin_nonce'),
7375 + 'nonce' => wp_create_nonce('mxchat_status_nonce'),
7382 7376 'admin_url' => admin_url(),
7383 7377 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
7384 7378 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
7385 7379 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
@@ -7386,14 +7380,10 @@
7386 7380 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
7387 7381 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7388 7382 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
7389 7383 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
7390 - 'add_phrase_nonce' => wp_create_nonce('mxchat_add_phrase_nonce'),
7391 - 'delete_phrase_nonce' => wp_create_nonce('mxchat_delete_phrase_nonce'),
7392 - 'get_phrases_nonce' => wp_create_nonce('mxchat_get_phrases_nonce'),
7393 - 'delete_legacy_nonce' => wp_create_nonce('mxchat_delete_legacy_nonce'),
7394 7384 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7395 - 'fetch_openrouter_models_nonce' => wp_create_nonce('mxchat_fetch_openrouter_models'),
7385 + 'fetch_openrouter_models_nonce' => wp_create_nonce('mxchat_fetch_openrouter_models'), // ADD THIS LINE
7396 7386 'is_activated' => $this->is_activated ? '1' : '0',
7397 7387 'status_refresh_interval' => 5000,
7398 7388 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7399 7389 'ajaxurl' => admin_url('admin-ajax.php')
@@ -7433,13 +7423,11 @@
7433 7423 'queue_nonce' => wp_create_nonce('mxchat_queue_nonce'),
7434 7424 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
7435 7425 'settings_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7436 7426 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7437 - 'entries_nonce' => wp_create_nonce('mxchat_entries_nonce'),
7438 7427 'admin_url' => admin_url(),
7439 7428 'ajaxurl' => admin_url('admin-ajax.php'),
7440 - 'status_refresh_interval' => 2000,
7441 - 'bot_id' => isset($_GET['bot_id']) ? sanitize_text_field($_GET['bot_id']) : 'default'
7429 + 'status_refresh_interval' => 2000
7442 7430 ));
7443 7431 break;
7444 7432
7445 7433 case 'mxchat-activation':
@@ -7448,60 +7436,8 @@
7448 7436 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
7449 7437 ));
7450 7438 break;
7451 7439
7452 - case 'mxchat-content':
7453 - wp_localize_script('mxchat-content-js', 'mxchatContent', array(
7454 - 'ajaxUrl' => admin_url('admin-ajax.php'),
7455 - 'nonce' => wp_create_nonce('mxchat_content_nonce'),
7456 - 'settingNonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7457 - 'previewUrl' => home_url('/?p='),
7458 - 'isActivated' => $this->is_activated(),
7459 - 'hasAdvancedContent' => apply_filters('mxchat_content_pro_feature', false, 'seo_readability'),
7460 - 'hasGSC' => apply_filters('mxchat_content_pro_feature', false, 'gsc_integration'),
7461 - 'seoOptimize' => array(
7462 - 'meta_description' => ($options['seo_optimize_meta_desc'] ?? 'on') === 'on',
7463 - 'seo_title' => ($options['seo_optimize_seo_title'] ?? 'on') === 'on',
7464 - 'slug' => ($options['seo_optimize_slug'] ?? 'on') === 'on',
7465 - 'readability' => ($options['seo_optimize_readability'] ?? 'on') === 'on',
7466 - 'internal_links' => ($options['seo_optimize_internal_links'] ?? 'on') === 'on',
7467 - 'img_alt' => ($options['seo_optimize_img_alt'] ?? 'on') === 'on',
7468 - 'featured_img' => ($options['seo_optimize_featured_img'] ?? 'on') === 'on',
7469 - ),
7470 - 'i18n' => array(
7471 - 'generating' => __('Generating...', 'mxchat'),
7472 - 'planning' => __('Planning content structure...', 'mxchat'),
7473 - 'images' => __('Generating images...', 'mxchat'),
7474 - 'writing' => __('Writing full content...', 'mxchat'),
7475 - 'creating' => __('Creating WordPress post...', 'mxchat'),
7476 - 'done' => __('Content generated successfully!', 'mxchat'),
7477 - 'error' => __('An error occurred. Please try again.', 'mxchat'),
7478 - 'editSuccess' => __('Content updated successfully.', 'mxchat'),
7479 - 'promptEmpty' => __('Please enter a prompt.', 'mxchat'),
7480 - )
7481 - ));
7482 - break;
7483 -
7484 - case 'mxchat-transcripts':
7485 - // Get chart data for localization
7486 - $chart_data = $this->get_transcripts_chart_data();
7487 -
7488 - wp_localize_script('mxchat-transcripts-js', 'mxchatAdmin', array(
7489 - 'ajax_url' => admin_url('admin-ajax.php'),
7490 - 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
7491 - 'delete_nonce' => wp_create_nonce('mxchat_delete_chat_history'),
7492 - 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7493 - 'translate_nonce' => wp_create_nonce('mxchat_translate_messages')
7494 - ));
7495 -
7496 - // Localize chart data separately - use array_values to ensure proper JSON array encoding
7497 - wp_localize_script('mxchat-transcripts-js', 'mxchatChartData', array(
7498 - 'labels' => array_values($chart_data['labels']),
7499 - 'chats' => array_values($chart_data['chats']),
7500 - 'messages' => array_values($chart_data['messages'])
7501 - ));
7502 - break;
7503 -
7504 7440 case 'mxchat-settings':
7505 7441 default:
7506 7442 // Color picker and settings localization
7507 7443 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
@@ -7560,18 +7496,8 @@
7560 7496 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
7561 7497 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
7562 7498 }
7563 7499
7564 - if (isset($input['rag_sources_limit'])) {
7565 - $new_input['rag_sources_limit'] = absint($input['rag_sources_limit']); // Ensure it's an integer
7566 - $new_input['rag_sources_limit'] = min(max($new_input['rag_sources_limit'], 3), 10); // Enforce range 3-10
7567 - }
7568 -
7569 - if (isset($input['rag_chunks_limit'])) {
7570 - $new_input['rag_chunks_limit'] = absint($input['rag_chunks_limit']); // Ensure it's an integer
7571 - $new_input['rag_chunks_limit'] = min(max($new_input['rag_chunks_limit'], 8), 20); // Enforce range 8-20
7572 - }
7573 -
7574 7500 if (isset($input['xai_api_key'])) {
7575 7501 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
7576 7502 }
7577 7503
@@ -7585,14 +7511,8 @@
7585 7511 // If checkbox not checked, it won't be in $input, so set to 'off'
7586 7512 $new_input['enable_streaming_toggle'] = 'off';
7587 7513 }
7588 7514
7589 - if (isset($input['enable_web_search'])) {
7590 - $new_input['enable_web_search'] = ($input['enable_web_search'] === 'on') ? 'on' : 'off';
7591 - } else {
7592 - // If checkbox not checked, it won't be in $input, so set to 'off'
7593 - $new_input['enable_web_search'] = 'off';
7594 - }
7595 7515
7596 7516 if (isset($input['deepseek_api_key'])) {
7597 7517 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
7598 7518 }
@@ -7634,32 +7554,12 @@
7634 7554 if (isset($input['append_to_body'])) {
7635 7555 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
7636 7556 }
7637 7557
7638 - // Post type visibility settings
7639 - if (isset($input['post_type_visibility_mode'])) {
7640 - $allowed_modes = array('all', 'include', 'exclude');
7641 - $new_input['post_type_visibility_mode'] = in_array($input['post_type_visibility_mode'], $allowed_modes)
7642 - ? $input['post_type_visibility_mode']
7643 - : 'all';
7644 - }
7645 -
7646 - if (isset($input['post_type_visibility_list'])) {
7647 - if (is_array($input['post_type_visibility_list'])) {
7648 - $new_input['post_type_visibility_list'] = array_map('sanitize_key', $input['post_type_visibility_list']);
7649 - } else {
7650 - $new_input['post_type_visibility_list'] = array();
7651 - }
7652 - }
7653 -
7654 7558 if (isset($input['contextual_awareness_toggle'])) {
7655 7559 $new_input['contextual_awareness_toggle'] = $input['contextual_awareness_toggle'] === 'on' ? 'on' : 'off';
7656 7560 }
7657 7561
7658 - if (isset($input['citation_links_toggle'])) {
7659 - $new_input['citation_links_toggle'] = $input['citation_links_toggle'] === 'on' ? 'on' : 'off';
7660 -}
7661 -
7662 7562 if (isset($input['top_bar_title'])) {
7663 7563 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
7664 7564 }
7665 7565
@@ -7750,9 +7650,9 @@
7750 7650 'text-embedding-ada-002',
7751 7651 'text-embedding-3-small',
7752 7652 'text-embedding-3-large',
7753 7653 'voyage-3-large',
7754 - 'gemini-embedding-001'
7654 + 'gemini-embedding-exp-03-07'
7755 7655 );
7756 7656 if (in_array($input['embedding_model'], $allowed_models)) {
7757 7657 $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
7758 7658 }
@@ -7762,13 +7662,8 @@
7762 7662 if ($input['model'] === 'openrouter') {
7763 7663 $new_input['model'] = 'openrouter';
7764 7664 } else {
7765 7665 $allowed_models = array(
7766 - 'gemini-3-pro-preview',
7767 - 'gemini-3-flash-preview',
7768 - 'gemini-2.5-pro',
7769 - 'gemini-2.5-flash',
7770 - 'gemini-2.5-flash-lite',
7771 7666 'gemini-2.0-flash',
7772 7667 'gemini-2.0-flash-lite',
7773 7668 'gemini-1.5-pro',
7774 7669 'gemini-1.5-flash',
@@ -7780,33 +7675,35 @@
7780 7675 'grok-3-mini-beta',
7781 7676 'grok-3-mini-fast-beta',
7782 7677 'grok-2',
7783 7678 'deepseek-chat',
7784 - 'claude-opus-4-6',
7785 - 'claude-sonnet-4-6',
7786 - 'claude-opus-4-5',
7787 7679 'claude-sonnet-4-5-20250929',
7788 7680 'claude-opus-4-1-20250805',
7789 7681 'claude-haiku-4-5-20251001',
7790 7682 'claude-opus-4-20250514',
7791 7683 'claude-sonnet-4-20250514',
7792 - 'gpt-5.2',
7793 - 'gpt-5.1-chat-latest',
7684 + 'claude-3-7-sonnet-20250219',
7685 + // REMOVED: 'claude-3-5-sonnet-20241022',
7686 + 'claude-3-opus-20240229',
7687 + 'claude-3-sonnet-20240229',
7688 + 'claude-3-haiku-20240307',
7794 7689 'gpt-5.1-2025-11-13',
7795 7690 'gpt-5',
7796 7691 'gpt-5-mini',
7797 7692 'gpt-5-nano',
7798 - 'gpt-5.4',
7799 - 'gpt-5.4-mini',
7800 - 'gpt-5.4-nano',
7801 - 'gpt-5.3-chat-latest',
7693 + 'gpt-4.1-2025-04-14',
7694 + 'gpt-4o',
7695 + 'gpt-4o-mini',
7696 + 'gpt-4-turbo',
7697 + 'gpt-4',
7698 + 'gpt-3.5-turbo',
7802 7699 );
7803 -
7700 +
7804 7701 if (in_array($input['model'], $allowed_models)) {
7805 7702 $new_input['model'] = sanitize_text_field($input['model']);
7806 7703 } else {
7807 7704 // Fallback for any deprecated model
7808 - $new_input['model'] = 'gpt-5.1-chat-latest';
7705 + $new_input['model'] = 'claude-3-7-sonnet-20250219';
7809 7706 }
7810 7707 }
7811 7708 }
7812 7709
@@ -7824,9 +7721,16 @@
7824 7721 $new_input['openrouter_api_key'] = sanitize_text_field($input['openrouter_api_key']);
7825 7722 }
7826 7723
7827 7724
7725 + if (isset($input['close_button_color'])) {
7726 + $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
7727 + }
7828 7728
7729 + if (isset($input['chatbot_bg_color'])) {
7730 + $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
7731 + }
7732 +
7829 7733 if (isset($input['woocommerce_consumer_key'])) {
7830 7734 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
7831 7735 }
7832 7736
@@ -7833,9 +7737,76 @@
7833 7737 if (isset($input['woocommerce_consumer_secret'])) {
7834 7738 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
7835 7739 }
7836 7740
7741 + if (isset($input['user_message_bg_color'])) {
7742 + $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
7743 + }
7837 7744
7745 + if (isset($input['user_message_font_color'])) {
7746 + $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
7747 + }
7748 +
7749 + if (isset($input['bot_message_bg_color'])) {
7750 + $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
7751 + }
7752 +
7753 + if (isset($input['bot_message_font_color'])) {
7754 + $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
7755 + }
7756 +
7757 + if (isset($input['live_agent_message_bg_color'])) {
7758 + $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
7759 + }
7760 +
7761 + if (isset($input['live_agent_message_font_color'])) {
7762 + $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
7763 + }
7764 +
7765 + if (isset($input['mode_indicator_bg_color'])) {
7766 + $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
7767 + }
7768 +
7769 + if (isset($input['mode_indicator_font_color'])) {
7770 + $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
7771 + }
7772 +
7773 + if (isset($input['toolbar_icon_color'])) {
7774 + $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
7775 + }
7776 +
7777 + if (isset($input['top_bar_bg_color'])) {
7778 + $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
7779 + }
7780 +
7781 + if (isset($input['quick_questions_toggle_color'])) {
7782 + $new_input['quick_questions_toggle_color'] = sanitize_hex_color($input['quick_questions_toggle_color']);
7783 + }
7784 +
7785 + if (isset($input['send_button_font_color'])) {
7786 + $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
7787 + }
7788 +
7789 + if (isset($input['chatbot_background_color'])) {
7790 + $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
7791 + }
7792 +
7793 + if (isset($input['icon_color'])) {
7794 + $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
7795 + }
7796 +
7797 + if (isset($input['custom_icon'])) {
7798 + $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
7799 + }
7800 +
7801 + if (isset($input['title_icon'])) {
7802 + $new_input['title_icon'] = esc_url_raw($input['title_icon']);
7803 + }
7804 +
7805 + if (isset($input['chat_input_font_color'])) {
7806 + $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
7807 + }
7808 +
7838 7809 // Sanitize link_target_toggle
7839 7810 if (isset($input['link_target_toggle'])) {
7840 7811 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
7841 7812 }
@@ -7968,226 +7939,12 @@
7968 7939 if (isset($input['live_agent_notification_message'])) {
7969 7940 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
7970 7941 }
7971 7942
7972 - // Telegram Integration
7973 - if (isset($input['telegram_status'])) {
7974 - $new_input['telegram_status'] = ($input['telegram_status'] === 'on') ? 'on' : 'off';
7975 - }
7976 - if (isset($input['telegram_bot_token'])) {
7977 - $new_input['telegram_bot_token'] = sanitize_text_field($input['telegram_bot_token']);
7978 - }
7979 - if (isset($input['telegram_group_id'])) {
7980 - $new_input['telegram_group_id'] = sanitize_text_field($input['telegram_group_id']);
7981 - }
7982 - if (isset($input['telegram_webhook_secret'])) {
7983 - $new_input['telegram_webhook_secret'] = sanitize_text_field($input['telegram_webhook_secret']);
7984 - }
7985 - if (isset($input['telegram_notification_message'])) {
7986 - $new_input['telegram_notification_message'] = sanitize_textarea_field($input['telegram_notification_message']);
7987 - }
7988 - if (isset($input['telegram_away_message'])) {
7989 - $new_input['telegram_away_message'] = sanitize_textarea_field($input['telegram_away_message']);
7990 - }
7991 -
7992 - // Sanitize script loading strategy
7993 - if (isset($input['script_loading_strategy'])) {
7994 - $allowed_strategies = array('default', 'defer', 'delay_1s', 'delay_3s', 'delay_5s', 'on_interaction');
7995 - $new_input['script_loading_strategy'] = in_array($input['script_loading_strategy'], $allowed_strategies)
7996 - ? $input['script_loading_strategy']
7997 - : 'default';
7998 - }
7999 -
8000 - // Sanitize debug mode
8001 - if (isset($input['debug_mode'])) {
8002 - $new_input['debug_mode'] = ($input['debug_mode'] === 'on') ? 'on' : 'off';
8003 - }
8004 -
8005 - // Content Generator Settings
8006 - if (isset($input['content_model'])) {
8007 - $new_input['content_model'] = sanitize_text_field($input['content_model']);
8008 - }
8009 - if (isset($input['content_image_model'])) {
8010 - $new_input['content_image_model'] = sanitize_text_field($input['content_image_model']);
8011 - }
8012 - if (isset($input['content_enable_images'])) {
8013 - $new_input['content_enable_images'] = ($input['content_enable_images'] === 'on') ? 'on' : 'off';
8014 - }
8015 - if (isset($input['content_use_placeholders'])) {
8016 - $new_input['content_use_placeholders'] = ($input['content_use_placeholders'] === 'on') ? 'on' : 'off';
8017 - }
8018 - if (isset($input['content_internal_linking'])) {
8019 - $new_input['content_internal_linking'] = ($input['content_internal_linking'] === 'on') ? 'on' : 'off';
8020 - }
8021 - if (isset($input['content_tool_use'])) {
8022 - $new_input['content_tool_use'] = ($input['content_tool_use'] === 'on') ? 'on' : 'off';
8023 - }
8024 -
8025 - // SEO Optimize toggle fields
8026 - foreach (array('seo_optimize_meta_desc', 'seo_optimize_seo_title', 'seo_optimize_slug', 'seo_optimize_readability', 'seo_optimize_internal_links', 'seo_optimize_img_alt', 'seo_optimize_featured_img') as $seo_key) {
8027 - if (isset($input[$seo_key])) {
8028 - $new_input[$seo_key] = ($input[$seo_key] === 'on') ? 'on' : 'off';
8029 - }
8030 - }
8031 -
8032 - // Preserve content generator settings when saving from main settings page
8033 - // (where content fields are not in the form submission)
8034 - $existing = get_option('mxchat_options', array());
8035 - foreach (array('content_model', 'content_image_model', 'content_enable_images', 'content_use_placeholders', 'content_internal_linking', 'content_tool_use', 'seo_optimize_meta_desc', 'seo_optimize_seo_title', 'seo_optimize_slug', 'seo_optimize_readability', 'seo_optimize_internal_links', 'seo_optimize_img_alt', 'seo_optimize_featured_img') as $key) {
8036 - if (!isset($new_input[$key]) && isset($existing[$key])) {
8037 - $new_input[$key] = $existing[$key];
8038 - }
8039 - }
8040 -
8041 7943 return $new_input;
8042 7944 }
8043 7945
8044 -/**
8045 - * Log a debug message if debug mode is enabled
8046 - *
8047 - * @param string $type The type of log entry (settings_save, api_error, activation, etc.)
8048 - * @param string $message The log message
8049 - * @param array $data Optional additional data to log
8050 - * @return bool Whether the message was logged
8051 - */
8052 -public static function mxchat_log_debug( $type, $message, $data = array() ) {
8053 - $options = get_option( 'mxchat_options', array() );
8054 7946
8055 - // Check if debug mode is enabled
8056 - if ( ! isset( $options['debug_mode'] ) || $options['debug_mode'] !== 'on' ) {
8057 - return false;
8058 - }
8059 -
8060 - // Get current log
8061 - $log = get_option( 'mxchat_debug_log', array() );
8062 - if ( ! is_array( $log ) ) {
8063 - $log = array();
8064 - }
8065 -
8066 - // Add new entry
8067 - $entry = array(
8068 - 'time' => current_time( 'Y-m-d H:i:s' ),
8069 - 'type' => sanitize_key( $type ),
8070 - 'message' => sanitize_text_field( $message ),
8071 - );
8072 -
8073 - if ( ! empty( $data ) ) {
8074 - $entry['data'] = $data;
8075 - }
8076 -
8077 - // Add to beginning of array (newest first)
8078 - array_unshift( $log, $entry );
8079 -
8080 - // Keep only last 100 entries
8081 - if ( count( $log ) > 100 ) {
8082 - $log = array_slice( $log, 0, 100 );
8083 - }
8084 -
8085 - // Save log
8086 - update_option( 'mxchat_debug_log', $log, false );
8087 -
8088 - return true;
8089 -}
8090 -
8091 -/**
8092 - * Get the debug log entries
8093 - *
8094 - * @return array The debug log entries
8095 - */
8096 -public static function mxchat_get_debug_log() {
8097 - $log = get_option( 'mxchat_debug_log', array() );
8098 - return is_array( $log ) ? $log : array();
8099 -}
8100 -
8101 -/**
8102 - * Clear the debug log
8103 - *
8104 - * @return bool Whether the log was cleared
8105 - */
8106 -public static function mxchat_clear_debug_log() {
8107 - return delete_option( 'mxchat_debug_log' );
8108 -}
8109 -
8110 -/**
8111 - * Export settings as JSON with masked API keys
8112 - *
8113 - * @return array The sanitized settings array
8114 - */
8115 -public static function mxchat_export_settings() {
8116 - $options = get_option( 'mxchat_options', array() );
8117 -
8118 - if ( ! is_array( $options ) ) {
8119 - return array();
8120 - }
8121 -
8122 - // List of API key fields to mask
8123 - $api_key_fields = array(
8124 - 'api_key',
8125 - 'xai_api_key',
8126 - 'claude_api_key',
8127 - 'deepseek_api_key',
8128 - 'voyage_api_key',
8129 - 'gemini_api_key',
8130 - 'openrouter_api_key',
8131 - 'loops_api_key',
8132 - 'brave_api_key',
8133 - 'live_agent_secret_key',
8134 - 'live_agent_bot_token',
8135 - 'telegram_bot_token',
8136 - 'telegram_webhook_secret',
8137 - 'woocommerce_consumer_key',
8138 - 'woocommerce_consumer_secret',
8139 - );
8140 -
8141 - // Mask API keys (show only last 4 characters)
8142 - foreach ( $api_key_fields as $field ) {
8143 - if ( isset( $options[ $field ] ) && ! empty( $options[ $field ] ) ) {
8144 - $value = $options[ $field ];
8145 - if ( strlen( $value ) > 4 ) {
8146 - $options[ $field ] = str_repeat( '*', strlen( $value ) - 4 ) . substr( $value, -4 );
8147 - } else {
8148 - $options[ $field ] = '****';
8149 - }
8150 - }
8151 - }
8152 -
8153 - // Add metadata
8154 - $export = array(
8155 - 'plugin_version' => defined( 'MXCHAT_VERSION' ) ? MXCHAT_VERSION : 'unknown',
8156 - 'export_date' => current_time( 'Y-m-d H:i:s' ),
8157 - 'wordpress_version' => get_bloginfo( 'version' ),
8158 - 'php_version' => phpversion(),
8159 - 'settings' => $options,
8160 - );
8161 -
8162 - return $export;
8163 -}
8164 -
8165 -/**
8166 - * Reset all settings to defaults
8167 - *
8168 - * @return bool Whether the reset was successful
8169 - */
8170 -public static function mxchat_reset_all_settings() {
8171 - // Delete main options
8172 - $deleted = delete_option( 'mxchat_options' );
8173 -
8174 - // Also clear the debug log
8175 - delete_option( 'mxchat_debug_log' );
8176 -
8177 - // Log the reset (will create new log since we just cleared it)
8178 - // We need to temporarily enable debug mode to log this
8179 - $temp_options = array( 'debug_mode' => 'on' );
8180 - update_option( 'mxchat_options', $temp_options );
8181 -
8182 - self::mxchat_log_debug( 'reset', 'All settings have been reset to defaults' );
8183 -
8184 - // Now delete again to trigger re-initialization
8185 - delete_option( 'mxchat_options' );
8186 -
8187 - return $deleted;
8188 -}
8189 -
8190 7947 // Method to append the chatbot to the body
8191 7948 public function mxchat_append_chatbot_to_body() {
8192 7949 $options = get_option('mxchat_options');
8193 7950 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
@@ -8257,65 +8014,73 @@
8257 8014 //error_log('DEBUG: Permission check failed');
8258 8015 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
8259 8016 }
8260 8017
8261 - // Get bot_id and content type filter from POST data
8018 + // Get bot_id from POST data
8262 8019 $bot_id = isset($_POST['bot_id']) ? sanitize_text_field($_POST['bot_id']) : 'default';
8263 - $content_type_filter = isset($_POST['content_type_filter']) ? sanitize_text_field($_POST['content_type_filter']) : '';
8264 -
8020 + //error_log('DEBUG: Bot ID from POST: ' . $bot_id);
8021 +
8265 8022 $success = true;
8266 8023 $error_messages = array();
8267 -
8024 +
8268 8025 // Get bot-specific Pinecone configuration
8269 8026 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
8270 8027 $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($bot_id);
8271 -
8028 +
8029 + //error_log('DEBUG: Pinecone options retrieved:');
8030 + //error_log(' - Use Pinecone: ' . ($pinecone_options['mxchat_use_pinecone'] ?? 'NOT SET'));
8031 + //error_log(' - API Key: ' . (empty($pinecone_options['mxchat_pinecone_api_key']) ? 'EMPTY' : 'SET'));
8032 + //error_log(' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
8033 +
8272 8034 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
8273 -
8035 +
8274 8036 if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
8275 - // Delete from Pinecone (with optional content type filter)
8276 - $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options, $content_type_filter);
8277 -
8037 + //error_log('[MXCHAT-DELETE] Pinecone is enabled for bot ' . $bot_id . ' - deleting all from Pinecone');
8038 +
8039 + // Delete from bot-specific Pinecone index
8040 + $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
8041 +
8042 + //error_log('DEBUG: Delete all result: ' . ($result['success'] ? 'SUCCESS' : 'FAILED'));
8278 8043 if (!$result['success']) {
8044 + //error_log('DEBUG: Delete all error: ' . $result['message']);
8045 + }
8046 +
8047 + if (!$result['success']) {
8279 8048 $success = false;
8280 8049 $error_messages[] = $result['message'];
8281 8050 }
8282 -
8051 +
8052 + // No cache clearing needed since we removed caching
8053 +
8283 8054 } else {
8055 + //error_log('[MXCHAT-DELETE] Pinecone not enabled for bot ' . $bot_id . ' - deleting from WordPress database');
8056 +
8284 8057 // Delete from WordPress database
8285 8058 global $wpdb;
8286 8059 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
8287 -
8288 - // Build WHERE conditions
8289 - $where_clauses = array();
8290 - $where_values = array();
8291 -
8292 - // Bot filter
8060 +
8061 + // If multi-bot is active and we have a specific bot, delete only that bot's content
8293 8062 if ($bot_id !== 'default' && class_exists('MxChat_Multi_Bot_Manager')) {
8294 - $where_clauses[] = 'bot_id = %s';
8295 - $where_values[] = $bot_id;
8296 - }
8297 -
8298 - // Content type filter
8299 - if (!empty($content_type_filter)) {
8300 - $where_clauses[] = 'content_type = %s';
8301 - $where_values[] = $content_type_filter;
8302 - }
8303 -
8304 - if (!empty($where_clauses)) {
8305 - $where_sql = implode(' AND ', $where_clauses);
8306 - $result = $wpdb->query($wpdb->prepare("DELETE FROM {$table_name} WHERE {$where_sql}", $where_values));
8063 + $result = $wpdb->delete(
8064 + $table_name,
8065 + array('bot_id' => $bot_id),
8066 + array('%s')
8067 + );
8307 8068 } else {
8308 - // No filters — delete all
8069 + // Delete all content for default bot
8309 8070 $result = $wpdb->query("DELETE FROM {$table_name}");
8310 8071 }
8311 -
8072 +
8312 8073 if ($result === false) {
8313 8074 $success = false;
8314 8075 $error_messages[] = 'Failed to delete from WordPress database';
8315 8076 }
8316 8077 }
8078 +
8079 + // No cache clearing needed since we removed caching
8317 8080
8081 + //error_log('=== DELETE ALL DEBUG END ===');
8082 +
8318 8083 // Redirect back with a success message and bot_id
8319 8084 $redirect_url = add_query_arg(array(
8320 8085 'page' => 'mxchat-prompts',
8321 8086 'bot_id' => $bot_id,