PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.4.6
MxChat – AI Chatbot & Content Generation for WordPress v2.4.6
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | includes/class-mxchat-admin.php +4767 -2971 2.0.42.4.6 View file →
@@ -6,13 +6,15 @@
6 6 class MxChat_Admin {
7 7 private $options;
8 8 private $chat_count;
9 9 private $is_activated;
10 + private $knowledge_manager;
10 11
11 - public function __construct() {
12 + public function __construct($knowledge_manager = null) {
12 13 $this->options = get_option('mxchat_options');
13 14 $this->chat_count = get_option('mxchat_chat_count', 0);
14 15 $this->is_activated = $this->is_license_active();
16 + $this->knowledge_manager = $knowledge_manager;
15 17
16 18 // Initialize default options if they are not set
17 19 if (!$this->options) {
18 20 $this->initialize_default_options();
@@ -23,49 +25,41 @@
23 25 add_action('admin_init', array($this, 'mxchat_page_init'));
24 26 add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
25 27 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 28 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
27 - add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
28 29 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 30 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 31 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 - add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
32 32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 34 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 - add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
36 35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
37 - add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 36 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39 37 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
40 38 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
41 - add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
42 - add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
43 - add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
44 - add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 - add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 - add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 - add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 - add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
49 - add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
50 - if (isset($this->options['enable_woocommerce_integration']) &&
51 - ($this->options['enable_woocommerce_integration'] === '1' ||
52 - $this->options['enable_woocommerce_integration'] === 'on')) {
39 + add_action('admin_post_mxchat_edit_intent', array($this, 'mxchat_handle_edit_intent'));
40 + add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
41 + add_action('admin_init', array($this, 'mxchat_transcripts_page_init'));
42 + add_action('wp_ajax_dismiss_live_agent_notice', array($this, 'dismiss_live_agent_notice'));
53 43
54 - //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
55 - add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
56 - add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
57 - add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
58 - }
44 + add_action('admin_init', array($this, 'register_pinecone_settings'));
45 +
46 + add_action('admin_notices', array($this, 'display_admin_notices'));
47 +
48 + add_action('wp_ajax_mxchat_test_streaming_actual', [$this, 'mxchat_handle_test_streaming_actual']);
49 + add_action('wp_ajax_mxchat_test_streaming', [$this, 'mxchat_handle_test_streaming']); // Keep existing as fallback
50 +
51 + add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
59 52 }
60 53
61 - // Method to check if the license is active
62 54 private function is_license_active() {
63 - $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
64 - return $license_status === esc_html__('active', 'mxchat');
65 - }
55 + // Get the raw value without translation
56 + $license_status = get_option('mxchat_license_status', 'inactive');
66 57
67 - // Initialize default options
58 + // Check against multiple possible values, bypassing translation issues
59 + return ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
60 +}
61 +
68 62 private function initialize_default_options() {
69 63 $default_options = array(
70 64 'api_key' => '',
71 65 'xai_api_key' => '',
@@ -70,25 +64,46 @@
70 64 'api_key' => '',
71 65 'xai_api_key' => '',
72 66 'claude_api_key' => '',
73 67 'deepseek_api_key' => '',
74 - 'system_prompt_instructions' => esc_html__('[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind:
75 - - Your name is [Chatbot Name].
76 - - Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject.
77 - - Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs.
78 - - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
79 - - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'),
68 + 'voyage_api_key' => '',
69 + 'gemini_api_key' => '',
70 + 'enable_streaming_toggle' => 'on',
71 + 'embedding_model' => 'text-embedding-ada-002',
72 + '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:
73 +
74 + # Response Style - CRITICALLY IMPORTANT
75 + - MAXIMUM LENGTH: 1-3 short sentences per response
76 + - Ultra-concise: Get straight to the answer with no filler
77 + - No introductions like "Sure!" or "I\'d be happy to help"
78 + - No phrases like "based on my knowledge" or "according to information"
79 + - No explanatory text before giving the answer
80 + - No summaries or repetition
81 + - Hyperlink all URLs
82 + - Respond in user\'s language
83 + - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
84 +
85 + # Knowledge Base Requirements - PREVENT HALLUCINATIONS
86 + - ONLY answer questions using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
87 + - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
88 + - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
89 + - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
90 + - Better to admit insufficient information than provide inaccurate answers',
80 91 'model' => esc_html__('gpt-4o', 'mxchat'),
81 92 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
82 93 'role_rate_limits' => array(),
83 94 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
84 95 'enable_email_block' => '',
85 - 'email_blocker_header_content' => esc_html__("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
96 + 'email_blocker_header_content' => __("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
86 97 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
98 + 'enable_name_field' => 'off', // NEW
99 + 'name_field_placeholder' => esc_html__('Enter your name', 'mxchat'), // NEW
87 100 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
88 - 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
101 + 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
102 + 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
89 103 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
90 104 'append_to_body' => esc_html__('off', 'mxchat'),
105 + 'contextual_awareness_toggle' => 'off',
91 106 'close_button_color' => esc_html__('#fff', 'mxchat'),
92 107 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
93 108 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
94 109 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
@@ -105,17 +120,19 @@
105 120
106 121 // New fields for Loops Integration
107 122 'loops_api_key' => '',
108 123 'loops_mailing_list' => '',
109 - 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
110 - 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
124 + 'triggered_phrase_response' => __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
125 + 'email_capture_response' => __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
111 126 'popular_question_1' => '',
112 127 'popular_question_2' => '',
113 128 'popular_question_3' => '',
114 - 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
115 - 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
116 - 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
129 + 'pdf_intent_trigger_text' => __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
130 + 'pdf_intent_success_text' => __("I've processed the PDF. What questions do you have about it?", 'mxchat'),
131 + 'pdf_intent_error_text' => __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
117 132 'pdf_max_pages' => 69,
133 + 'show_pdf_upload_button' => 'on',
134 + 'show_word_upload_button' => 'on',
118 135
119 136 // Live Agent Integration
120 137 'live_agent_webhook_url' => '',
121 138 'live_agent_secret_key' => '',
@@ -149,10 +166,8 @@
149 166 // Update the $this->options property
150 167 $this->options = $merged_options;
151 168 }
152 169
153 -
154 -
155 170 public function mxchat_add_plugin_page() {
156 171 // Main menu page
157 172 add_menu_page(
158 173 esc_html__('MxChat Settings', 'mxchat'),
@@ -173,26 +188,24 @@
173 188 'mxchat-prompts',
174 189 array($this, 'mxchat_create_prompts_page')
175 190 );
176 191
177 - // Submenu page for Chat Transcripts
178 192 add_submenu_page(
179 - 'mxchat-max', // Corrected parent slug to match the main menu
193 + 'mxchat-max',
180 194 esc_html__('Chat Transcripts', 'mxchat'),
181 195 esc_html__('Transcripts', 'mxchat'),
182 196 'manage_options',
183 197 'mxchat-transcripts',
184 - array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
198 + array($this, 'mxchat_create_transcripts_page')
185 199 );
186 200
187 - // Submenu page for Intents
188 201 add_submenu_page(
189 - 'mxchat-max', // Parent slug
190 - esc_html__('MxChat Intents', 'mxchat'), // Page title
191 - esc_html__('Intents', 'mxchat'), // Menu title
192 - 'manage_options', // Capability
193 - 'mxchat-intents', // Menu slug
194 - array($this, 'mxchat_intents_page_html') // Callback function
202 + 'mxchat-max',
203 + esc_html__('MxChat Actions', 'mxchat'),
204 + esc_html__('Actions', 'mxchat'),
205 + 'manage_options',
206 + 'mxchat-actions',
207 + array($this, 'mxchat_actions_page_html')
195 208 );
196 209
197 210 add_submenu_page(
198 211 'mxchat-max',
@@ -219,156 +232,299 @@
219 232 $addons_page = new MxChat_Addons();
220 233 $addons_page->render_page();
221 234 }
222 235
223 -public function mxchat_save_setting_callback() {
224 - check_ajax_referer('mxchat_save_setting_nonce');
225 - if (!current_user_can('manage_options')) {
226 - wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
236 +/**
237 + * Test actual streaming functionality in the WordPress environment
238 + */
239 +public function mxchat_handle_test_streaming_actual() {
240 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
241 +
242 + // Check if headers have already been sent
243 + if (headers_sent()) {
244 + wp_send_json_error(['message' => 'Headers already sent - streaming not possible']);
245 + return;
227 246 }
228 - $name = isset($_POST['name']) ? $_POST['name'] : '';
229 - // Strip slashes from the value before saving
230 - $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
231 - if (empty($name)) {
232 - wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
247 +
248 + // Check for required functions
249 + if (!function_exists('curl_init')) {
250 + wp_send_json_error(['message' => 'cURL not available - streaming requires cURL']);
251 + return;
233 252 }
234 - // Load the full options array
253 +
254 + // Get user's selected model and API key
235 255 $options = get_option('mxchat_options', []);
236 - // Handle special cases
237 - switch ($name) {
238 - case 'additional_popular_questions':
239 - $questions = json_decode($value, true); // No need for stripslashes here
240 - if (is_array($questions)) {
241 - $options[$name] = $questions;
242 - // Also update old option for backwards compatibility
243 - update_option('additional_popular_questions', $questions);
244 - }
256 + $selected_model = $options['model'] ?? 'gpt-4o';
257 +
258 + // Get the provider from the model
259 + $model_parts = explode('-', $selected_model);
260 + $provider = strtolower($model_parts[0]);
261 +
262 + // Get the appropriate API key
263 + $api_key = '';
264 + switch ($provider) {
265 + case 'gpt':
266 + case 'o1':
267 + $api_key = $options['api_key'] ?? '';
245 268 break;
246 - case 'user_message_bg_color':
247 - case 'user_message_font_color':
248 - case 'bot_message_bg_color':
249 - case 'bot_message_font_color':
250 - case 'top_bar_bg_color':
251 - case 'send_button_font_color':
252 - case 'chatbot_background_color':
253 - case 'icon_color':
254 - case 'chat_input_font_color':
255 - case 'live_agent_message_bg_color':
256 - case 'live_agent_message_font_color':
257 - case 'mode_indicator_bg_color':
258 - case 'mode_indicator_font_color':
259 - case 'toolbar_icon_color':
260 - // Store color values directly
261 - $options[$name] = $value;
269 + case 'claude':
270 + $api_key = $options['claude_api_key'] ?? '';
262 271 break;
263 - case 'live_agent_status':
264 - // Set the new value
265 - $options[$name] = ($value === 'on') ? 'on' : 'off';
272 + case 'grok':
273 + $api_key = $options['xai_api_key'] ?? '';
266 274 break;
267 - case 'enable_woocommerce_integration':
268 - // Handle values that used to be 1/0
269 - $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
275 + case 'deepseek':
276 + $api_key = $options['deepseek_api_key'] ?? '';
270 277 break;
278 + case 'gemini':
279 + $api_key = $options['gemini_api_key'] ?? '';
280 + break;
271 281 default:
272 - // Handle role rate limits
273 - if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
274 - // Extract role ID from the name
275 - preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
276 - if (isset($matches[1])) {
277 - $role_id = $matches[1];
278 - // Initialize role_rate_limits if it doesn't exist
279 - if (!isset($options['role_rate_limits'])) {
280 - $options['role_rate_limits'] = [];
281 - }
282 - // Update the specific role's rate limit
283 - $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
284 - break;
285 - }
286 - }
287 - // Handle toggles
288 - if (strpos($name, 'toggle') !== false || in_array($name, [
289 - 'chat_persistence_toggle',
290 - 'privacy_toggle',
291 - 'complianz_toggle',
292 - 'chat_toolbar_toggle' // Removed live_agent_status from here
293 - ])) {
294 - $options[$name] = ($value === 'on') ? 'on' : 'off';
295 - } else {
296 - // Store all other values directly
297 - $options[$name] = $value;
298 - }
282 + // Default to OpenAI for unknown models
283 + $api_key = $options['api_key'] ?? '';
284 + $provider = 'gpt';
299 285 break;
300 286 }
301 - // Save all updates
302 - $updated = update_option('mxchat_options', $options);
303 - // Handle backwards compatibility for certain fields
304 - $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
305 - 'brave_country', 'brave_language', 'similarity_threshold'];
306 - if (in_array($name, $legacy_fields)) {
307 - // Also update the individual option for backwards compatibility
308 - update_option($name, $value);
287 +
288 + if (empty($api_key)) {
289 + wp_send_json_error(['message' => "API key not configured for {$provider} provider"]);
290 + return;
309 291 }
310 - if ($updated) {
311 - wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
312 - } else {
313 - wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
292 +
293 + // Test streaming with the selected model and provider
294 + try {
295 + $this->perform_streaming_test($provider, $selected_model, $api_key);
296 + } catch (Exception $e) {
297 + wp_send_json_error(['message' => 'Streaming test exception: ' . $e->getMessage()]);
314 298 }
315 299 }
316 300
317 301 /**
318 - * Handles AJAX auto-save for prompts and auto-sync settings.
302 + * Perform the actual streaming test
319 303 */
320 -public function mxchat_save_prompts_setting_callback() {
321 - // Check the correct nonce for this action.
322 - check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
304 +private function perform_streaming_test($provider, $model, $api_key) {
305 + // Set streaming headers
306 + header('Content-Type: text/event-stream');
307 + header('Cache-Control: no-cache');
308 + header('X-Accel-Buffering: no'); // Disable nginx buffering
323 309
324 - if ( ! current_user_can( 'manage_options' ) ) {
325 - wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
310 + // Prepare test message
311 + $test_message = "Please respond with exactly: 'Streaming test successful!' - send this as a short response for testing.";
312 +
313 + // Configure API request based on provider
314 + $url = '';
315 + $headers = [];
316 + $body = [];
317 +
318 + switch ($provider) {
319 + case 'gpt':
320 + case 'o1':
321 + $url = 'https://api.openai.com/v1/chat/completions';
322 + $headers = [
323 + 'Content-Type: application/json',
324 + 'Authorization: Bearer ' . $api_key
325 + ];
326 + $body = [
327 + 'model' => $model,
328 + 'messages' => [['role' => 'user', 'content' => $test_message]],
329 + 'max_tokens' => 50,
330 + 'temperature' => 0.3,
331 + 'stream' => true
332 + ];
333 + break;
334 +
335 + case 'claude':
336 + $url = 'https://api.anthropic.com/v1/messages';
337 + $headers = [
338 + 'Content-Type: application/json',
339 + 'x-api-key: ' . $api_key,
340 + 'anthropic-version: 2023-06-01'
341 + ];
342 + $body = [
343 + 'model' => $model,
344 + 'messages' => [['role' => 'user', 'content' => $test_message]],
345 + 'max_tokens' => 50,
346 + 'temperature' => 0.3,
347 + 'stream' => true
348 + ];
349 + break;
350 +
351 + case 'grok':
352 + $url = 'https://api.x.ai/v1/chat/completions';
353 + $headers = [
354 + 'Content-Type: application/json',
355 + 'Authorization: Bearer ' . $api_key
356 + ];
357 + $body = [
358 + 'model' => $model,
359 + 'messages' => [['role' => 'user', 'content' => $test_message]],
360 + 'max_tokens' => 50,
361 + 'temperature' => 0.3,
362 + 'stream' => true
363 + ];
364 + break;
365 +
366 + case 'deepseek':
367 + $url = 'https://api.deepseek.com/v1/chat/completions';
368 + $headers = [
369 + 'Content-Type: application/json',
370 + 'Authorization: Bearer ' . $api_key
371 + ];
372 + $body = [
373 + 'model' => $model,
374 + 'messages' => [['role' => 'user', 'content' => $test_message]],
375 + 'max_tokens' => 50,
376 + 'temperature' => 0.3,
377 + 'stream' => true
378 + ];
379 + break;
380 +
381 + default:
382 + echo "data: " . json_encode(['error' => 'Unsupported provider for streaming test: ' . $provider]) . "\n\n";
383 + flush();
384 + return;
326 385 }
327 386
328 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
329 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
387 + // Initialize cURL
388 + $ch = curl_init();
389 + curl_setopt($ch, CURLOPT_URL, $url);
390 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
391 + curl_setopt($ch, CURLOPT_POST, true);
392 + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
393 + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
394 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
395 + curl_setopt($ch, CURLOPT_TIMEOUT, 30);
396 + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use ($provider) {
397 + return $this->process_streaming_test_data($data, $provider);
398 + });
330 399
331 - if ( empty( $name ) ) {
332 - wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
400 + // Send initial test message
401 + echo "data: " . json_encode(['content' => '[Starting streaming test...]']) . "\n\n";
402 + flush();
403 +
404 + $result = curl_exec($ch);
405 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
406 + $curl_error = curl_error($ch);
407 + curl_close($ch);
408 +
409 + if ($curl_error) {
410 + echo "data: " . json_encode(['error' => 'cURL Error: ' . $curl_error]) . "\n\n";
411 + flush();
412 + return;
333 413 }
334 414
335 - // Handle standalone auto-sync settings.
336 - if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
337 - // Convert checkbox value to "1" if checked, "0" if not.
338 - $value = ( $value === 'on' ) ? '1' : '0';
339 - update_option( $name, $value );
340 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
415 + if ($http_code !== 200) {
416 + echo "data: " . json_encode(['error' => 'API returned HTTP ' . $http_code]) . "\n\n";
417 + flush();
418 + return;
341 419 }
342 420
343 - // Handle fields stored in the 'mxchat_prompts_options' array.
344 -// Handle fields stored in the 'mxchat_prompts_options' array.
345 -if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
346 - // Extract the key name using regex.
347 - if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
348 - $key = sanitize_text_field( $matches[1] );
349 - $options = get_option( 'mxchat_prompts_options', [] );
421 + // Send completion signal
422 + echo "data: [DONE]\n\n";
423 + flush();
424 +}
350 425
351 - // Remove Pinecone-specific settings from the main plugin handling.
352 - if ( 'mxchat_use_pinecone' === $key ) {
353 - // Optionally, you could simply ignore this field or set a default value.
354 - // For example, ensure it's always 0 or remove it entirely:
355 - // $options[ $key ] = '0';
356 - // Or, if you prefer, just return an error so that the main plugin doesn't handle it:
357 - wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
426 +/**
427 + * Process streaming data for the test
428 + */
429 +private function process_streaming_test_data($data, $provider) {
430 + static $chunk_count = 0;
431 +
432 + $lines = explode("\n", $data);
433 +
434 + foreach ($lines as $line) {
435 + if (trim($line) === '') {
436 + continue;
437 + }
438 +
439 + // Handle different provider formats
440 + if ($provider === 'claude') {
441 + // Claude uses event: and data: format
442 + if (strpos($line, 'data: ') === 0) {
443 + $json_str = substr($line, 6);
444 + $json = json_decode($json_str, true);
445 +
446 + if (isset($json['type']) && $json['type'] === 'content_block_delta') {
447 + if (isset($json['delta']['text'])) {
448 + $chunk_count++;
449 + echo "data: " . json_encode([
450 + 'content' => $json['delta']['text'],
451 + 'test_chunk' => $chunk_count
452 + ]) . "\n\n";
453 + flush();
454 + }
455 + }
456 + }
358 457 } else {
359 - // For all other fields, use the existing logic.
360 - $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
458 + // OpenAI, X.AI, DeepSeek format
459 + if (strpos($line, 'data: ') === 0) {
460 + $json_str = substr($line, 6);
461 +
462 + if ($json_str === '[DONE]') {
463 + // Don't echo [DONE] here, let the main function handle it
464 + continue;
465 + }
466 +
467 + $json = json_decode($json_str, true);
468 + if (isset($json['choices'][0]['delta']['content'])) {
469 + $chunk_count++;
470 + echo "data: " . json_encode([
471 + 'content' => $json['choices'][0]['delta']['content'],
472 + 'test_chunk' => $chunk_count
473 + ]) . "\n\n";
474 + flush();
475 + }
476 + }
361 477 }
362 - update_option( 'mxchat_prompts_options', $options );
363 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
364 478 }
479 +
480 + return strlen($data);
365 481 }
366 482
483 +/**
484 + * Updated version of your existing test method (keep this as a fallback)
485 + */
486 +public function mxchat_handle_test_streaming() {
487 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
367 488
368 - wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
489 + // Use the actual streaming test instead
490 + $this->mxchat_handle_test_streaming_actual();
369 491 }
370 492
493 +
494 +public function register_pinecone_settings() {
495 + register_setting(
496 + 'mxchat_pinecone_addon_options',
497 + 'mxchat_pinecone_addon_options',
498 + array(
499 + 'type' => 'array',
500 + 'sanitize_callback' => array($this, 'sanitize_pinecone_settings'),
501 + 'default' => array(
502 + 'mxchat_use_pinecone' => '0',
503 + 'mxchat_pinecone_api_key' => '',
504 + 'mxchat_pinecone_host' => '',
505 + 'mxchat_pinecone_index' => '',
506 + 'mxchat_pinecone_environment' => ''
507 + )
508 + )
509 + );
510 +}
511 +
512 +public function sanitize_pinecone_settings($input) {
513 + $sanitized = array();
514 +
515 + $sanitized['mxchat_use_pinecone'] = isset($input['mxchat_use_pinecone']) ? '1' : '0';
516 + $sanitized['mxchat_pinecone_api_key'] = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
517 + $sanitized['mxchat_pinecone_host'] = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
518 + $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
519 + $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
520 +
521 + // Remove https:// from host if present
522 + $sanitized['mxchat_pinecone_host'] = str_replace(['https://', 'http://'], '', $sanitized['mxchat_pinecone_host']);
523 +
524 + return $sanitized;
525 +}
526 +
371 527 public function mxchat_display_admin_notice() {
372 528 // Success notice
373 529 if ($message = get_transient('mxchat_admin_notice_success')) {
374 530 ?>
@@ -391,2035 +547,2320 @@
391 547 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
392 548 }
393 549 }
394 550
551 +public function show_live_agent_disabled_banner() {
552 + $show_disabled_notice = get_option('mxchat_show_live_agent_disabled_notice', false);
395 553
396 -
397 -
398 -
554 + if ($show_disabled_notice) {
555 + ?>
556 + <div class="mxchat-live-agent-disabled-notice" id="mxchat-disabled-notice">
557 + <div class="mxchat-pro-notification">
558 + <button type="button" class="mxchat-dismiss-btn" onclick="dismissLiveAgentNotice()" aria-label="Dismiss notification">
559 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
560 + <line x1="18" y1="6" x2="6" y2="18"></line>
561 + <line x1="6" y1="6" x2="18" y2="18"></line>
562 + </svg>
563 + </button>
564 + <div class="mxchat-live-agent-content">
565 + <h3>🔧 Live Agent Integration Updated!</h3>
566 + <p>We've temporarily disabled your Live Agent integration due to recent enhancements that have made it much better! You can easily turn it back on by going to <strong>Toolbar & Components → Live Agent Settings</strong> and reviewing the new configuration options.</p>
567 + </div>
568 + </div>
569 + </div>
570 + <?php
571 + }
572 +}
399 573 public function mxchat_create_admin_page() {
574 + $this->add_live_agent_nonce();
400 575
401 576 ?>
402 - <div class="wrap mxchat-admin">
403 - <?php if (!$this->is_activated): ?>
404 - <div class="mxchat-pro-banner">
405 - <p>
406 - <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
407 - <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
408 - </p>
577 + <div class="wrap mxchat-wrapper">
578 + <!-- Hero Section -->
579 + <div class="mxchat-hero">
580 + <h1 class="mxchat-main-title">
581 + <span class="mxchat-gradient-text">MxChat</span> Settings
582 + </h1>
583 + <p class="mxchat-hero-subtitle">
584 + <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
585 + </p>
409 586 </div>
410 - <?php endif; ?>
411 587
412 - <div class="mxchat-agents-banner">
413 - <p>
414 - <strong><?php echo esc_html__('The MxChat Add-Ons Platform', 'mxchat'); ?></strong><?php echo esc_html__(' is here! Pro users get unlimited access to our growing collection of powerful add-ons. ', 'mxchat'); ?><a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>"><?php echo esc_html__('Explore all add-ons', 'mxchat'); ?></a><?php echo esc_html__(' including Forms Builder, Theme Customizer, Content Moderation, and more – all included with your lifetime Pro license!', 'mxchat'); ?>
415 - </p>
416 - </div>
588 + <div class="mxchat-content">
589 +
590 + <?php if (!$this->is_activated): ?>
591 + <div class="mxchat-pro-card">
592 + <div class="mxchat-pro-notification">
593 + <div class="mxchat-pro-content">
594 + <h3>🚀 Limited Lifetime Offer: Save 15% on MxChat Pro, Agency, or Agency Plus!</h3>
595 + <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>
596 + <div class="mxchat-pro-cta">
597 + <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade Today', 'mxchat'); ?></a>
598 + <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
599 + </div>
600 + </div>
601 + </div>
602 + <?php endif; ?>
417 603
418 - <h2 class="mxchat-nav-tab-wrapper">
419 - <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
420 - <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
421 - <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
422 - </h2>
604 + <?php
605 + $this->show_live_agent_disabled_banner();
606 + ?>
423 607
424 - <div id="chatbot" class="mxchat-tab-content active">
425 - <div class="mxchat-autosave-section">
426 - <?php do_settings_sections('mxchat-chatbot'); ?>
608 + <!-- Tabs Navigation -->
609 + <div class="mxchat-tabs">
610 + <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
611 + <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
612 + <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
427 613 </div>
428 - </div>
429 614
430 - <div id="embed" class="mxchat-tab-content">
431 - <div class="mxchat-autosave-section">
432 -
433 - <div class="mxchat-settings-section">
434 - <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
435 - <table class="form-table">
436 - <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
437 - </table>
615 + <!-- Tab Contents -->
616 + <div id="chatbot" class="mxchat-tab-content active">
617 + <div class="mxchat-card">
618 + <div class="mxchat-autosave-section">
619 + <?php do_settings_sections('mxchat-chatbot'); ?>
620 + </div>
438 621 </div>
622 + </div>
439 623
440 - <div class="section-divider"></div>
624 + <div id="embed" class="mxchat-tab-content">
441 625
442 - <div class="mxchat-settings-section">
443 - <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
444 - <table class="form-table">
445 - <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
446 - </table>
626 + <div class="mxchat-card">
627 + <h2><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h2>
628 + <div class="mxchat-autosave-section">
629 + <table class="form-table">
630 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
631 + </table>
632 + </div>
447 633 </div>
448 634
449 - <div class="section-divider"></div>
450 635
451 - <div class="mxchat-settings-section">
452 - <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
453 - <table class="form-table">
454 - <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
455 - </table>
636 + <div class="mxchat-card">
637 + <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
638 + <div class="mxchat-autosave-section">
639 + <table class="form-table">
640 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
641 + </table>
642 + </div>
456 643 </div>
457 644
458 - <div class="section-divider"></div>
645 + <div class="mxchat-card">
646 + <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
647 + <div class="mxchat-autosave-section">
648 + <table class="form-table">
649 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
650 + </table>
651 + </div>
652 + </div>
459 653
460 - <!-- Live Agent Settings Section -->
461 - <div class="mxchat-settings-section">
462 - <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
463 - <p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('to set up live agent transfer via Slack.', 'mxchat'); ?></p>
464 - <table class="form-table">
465 - <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
466 - </table>
654 + <div class="mxchat-card">
655 + <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
656 + <div class="mxchat-autosave-section">
657 +<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">
658 + <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
659 + </table>
660 + </div>
467 661 </div>
468 662 </div>
469 - </div>
470 663
664 + <div id="general" class="mxchat-tab-content">
665 + <div class="mxchat-card">
666 + <?php do_settings_sections('mxchat-general'); ?>
667 + <div class="video-tutorials-section">
471 668
472 - <div id="general" class="mxchat-tab-content">
473 - <?php do_settings_sections('mxchat-general'); ?>
474 - <p>
475 - <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
476 - <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
477 - <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
478 - </p>
479 - <p>
480 - <?php echo esc_html__('If you like our plugin, please consider', 'mxchat'); ?> <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('leaving us a review', 'mxchat'); ?></a>.
481 - </p>
482 669
483 - <div class="faq-item">
484 - <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
485 - <p>
486 - <?php echo esc_html__('The Claude API, provided by Anthropic, allows for intelligent, context-aware chatbot responses in MxChat. To use it, you will need both an OpenAI API key and a Claude API key. This is necessary because the system utilizes OpenAI for vector embedding in the Retrieval-Augmented Generation (RAG) process, as Claude does not currently offer an embedding API.', 'mxchat'); ?>
487 - </p>
488 - <p>
489 - <?php echo esc_html__('When using the Claude API, your custom content is sent to Claude for generating responses, while the embeddings are processed through OpenAI. This ensures your chatbot provides accurate and engaging responses while maintaining knowledge relevant to your website.', 'mxchat'); ?>
490 - </p>
491 - <p>
492 - <?php echo esc_html__('You can obtain your Claude API key by signing up on the', 'mxchat'); ?> <a href="https://www.anthropic.com/api" target="_blank" rel="noopener"><?php echo esc_html__('Anthropic API page', 'mxchat'); ?></a>.
493 - </p>
494 - </div>
670 + <div class="support-notification">
671 + <div class="support-notification-icon">
672 + <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">
673 + <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"/>
674 + <path d="M12 9v4"/>
675 + <path d="M12 17h.01"/>
676 + </svg>
677 + </div>
678 + <div class="support-notification-content">
679 + <h3 class="support-notification-title"><?php echo esc_html__('Need Help? We\'re Here for You!', 'mxchat'); ?></h3>
680 + <p class="support-notification-message">
681 + <?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'); ?>
682 + </p>
683 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer" class="support-notification-button">
684 + <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">
685 + <path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/>
686 + <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/>
687 + </svg>
688 + <?php echo esc_html__('Submit Support Ticket', 'mxchat'); ?>
689 + </a>
690 + </div>
691 +</div>
495 692
496 - <div class="faq-item">
497 - <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
498 - <p>
499 - <?php echo esc_html__('The X.AI API, released on 10.21.24, is currently in beta. To use it, you will need both an OpenAI API key and an X.AI API key. This is because OpenAI handles vector embeddings in the Retrieval-Augmented Generation (RAG) process, as X.AI does not yet provide an embedding API.', 'mxchat'); ?>
500 - </p>
501 - <p>
502 - <?php echo esc_html__('Custom content is sent to the X.AI API for generating responses, while OpenAI processes the embeddings. This allows the chatbot to provide advanced responses while maintaining context from your website. You can get your X.AI API key from the', 'mxchat'); ?> <a href="https://docs.x.ai/docs" target="_blank" rel="noopener"><?php echo esc_html__('X.AI API documentation', 'mxchat'); ?></a>.
503 - </p>
693 + <div class="tutorial-grid">
694 +
695 + <div class="tutorial-item">
696 + <h3><?php echo esc_html__('AI Theme Generator Tutorial', 'mxchat'); ?></h3>
697 + <div class="video-description">
698 + <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>
699 + <a href="https://www.youtube.com/watch?v=rSQDW2qbtRU&t" target="_blank" rel="noopener" class="video-link">
700 + <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>
701 + <?php echo esc_html__('Watch AI Theme Generator Tutorial', 'mxchat'); ?>
702 + </a>
703 + </div>
704 +</div>
705 +
706 +
707 + <div class="tutorial-item">
708 + <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
709 + <div class="video-description">
710 + <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
711 + <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
712 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
713 + <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
714 + </a>
504 715 </div>
716 + </div>
505 717
506 - <div class="faq-item">
507 - <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
508 - <p>
509 - <?php echo esc_html__('Yes, you will need an OpenAI API key to power the chatbot. You can obtain an API key by signing up on the', 'mxchat'); ?> <a href="https://platform.openai.com/signup" target="_blank" rel="noopener"><?php echo esc_html__('OpenAI platform', 'mxchat'); ?></a>. <?php echo esc_html__('After signing up, you must add credits to your account—typically, $5 in credits is sufficient to get started.', 'mxchat'); ?>
510 - </p>
511 - <p>
512 - <?php echo esc_html__('Once you have your API key, simply enter it in the chatbot\'s settings to enable functionality. The chatbot relies on OpenAI’s models for generating responses, so having sufficient credits in your OpenAI account is essential for smooth operation.', 'mxchat'); ?>
513 - </p>
514 - </div>
718 +<div class="tutorial-item">
719 + <h3><?php echo esc_html__('Admin Assistant Add-on', 'mxchat'); ?></h3>
720 + <div class="video-description">
721 + <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>
722 + <a href="https://youtu.be/AdEA1k-UCFM" target="_blank" rel="noopener" class="video-link">
723 + <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>
724 + <?php echo esc_html__('Watch Admin Assistant Tutorial', 'mxchat'); ?>
725 + </a>
726 + </div>
727 +</div>
515 728
516 - <div class="faq-item">
517 - <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
518 - <p>
519 - <?php echo esc_html__('You can add the chatbot using the shortcode', 'mxchat'); ?> <code>[mxchat_chatbot floating="yes"]</code> <?php echo esc_html__('or', 'mxchat'); ?> <code>[mxchat_chatbot floating="no"]</code>. <?php echo esc_html__('For initial testing and styling, it\'s best to use the shortcode on a draft or non-public page. Once you’re ready to go live, enable the “Append Chat Widget to Body” option in the settings for site-wide integration (recommended) or add shortcode to the footer.', 'mxchat'); ?>
520 - </p>
729 + <div class="tutorial-item">
730 + <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
731 + <div class="video-description">
732 + <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>
733 + <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
734 + <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>
735 + <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
736 + </a>
521 737 </div>
738 + </div>
522 739
523 - <div class="faq-item">
524 - <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
525 - <p>
526 - <?php echo esc_html__('The chatbot uses AI and vector embeddings to connect users with relevant information. When you submit content, it converts it into a mathematical format. When a user asks a question, the bot matches it to your stored content and generates a response based on relevance. For example, submitting "Our phone number is 910-123-4567" allows the bot to retrieve this information when asked about contact details. To ensure related information is provided together, submit it in one entry.', 'mxchat'); ?>
527 - </p>
740 + <div class="tutorial-item">
741 + <h3><?php echo esc_html__('Theme Customizer Add-on', 'mxchat'); ?></h3>
742 + <div class="video-description">
743 + <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>
744 + <a href="https://youtu.be/MfbB9mZi6ag" target="_blank" rel="noopener" class="video-link">
745 + <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>
746 + <?php echo esc_html__('Watch Theme Customizer Tutorial', 'mxchat'); ?>
747 + </a>
528 748 </div>
749 + </div>
529 750
530 - <div class="faq-item">
531 - <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
532 - <p>
533 - <?php echo esc_html__('Occasionally, the chatbot may generate inaccurate links or information, known as "hallucinations." To minimize this, you can add system instructions to guide its behavior. For example, include an instruction like: "Only provide links you directly have access to or retrieve from the knowledge base. Do not make up links or information that you do not have direct access to." This helps the bot stay aligned with your content.', 'mxchat'); ?>
534 - </p>
751 + <div class="tutorial-item">
752 + <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
753 + <div class="video-description">
754 + <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>
755 + <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
756 + <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>
757 + <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
758 + </a>
535 759 </div>
760 + </div>
536 761
537 - <div class="faq-item">
538 - <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
539 - <p>
540 - <?php echo esc_html__('Intents allow MxChat to recognize user requests and trigger specific actions, such as capturing emails or displaying product information. This helps provide a more interactive and responsive experience. For a detailed explanation of each intent, please refer to our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Intents Documentation', 'mxchat'); ?></a>.
541 - </p>
762 + <div class="tutorial-item">
763 + <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
764 + <div class="video-description">
765 + <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>
766 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
767 + <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
768 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
769 + <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
770 + </a>
542 771 </div>
772 + </div>
543 773
544 - <div class="faq-item">
545 - <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
546 - <p>
547 - <?php echo esc_html__('The Pro version offers direct integration with the Complianz GDPR plugin, making it easy to stay compliant with GDPR. If Complianz is enabled on your website, users must accept consent before the chatbot widget appears. The chatbot will only display once the user has accepted, ensuring compliance with data privacy regulations.', 'mxchat'); ?>
548 - </p>
774 + <div class="tutorial-item">
775 + <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
776 + <div class="video-description">
777 + <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>
778 + <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
779 + <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>
780 + <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
781 + </a>
549 782 </div>
783 + </div>
550 784
551 - <div class="faq-item">
552 - <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
553 - <p>
554 - <?php echo esc_html__('With WooCommerce integration, the chatbot automatically embeds new products and updates existing ones. For already-published products, you can click update or submit the product sitemap. The “Order History Access” setting allows the bot to access users\' order history (requires login) and assist with order inquiries. To enable the “Add to Cart” feature, add this system instruction: “After discussing a product, ask if the user wants to add it to their cart. The user must say \'Add to cart\' exactly.” Currently, this feature supports English only, with more languages coming soon.', 'mxchat'); ?>
555 - </p>
785 + <div class="tutorial-item">
786 + <h3><?php echo esc_html__('MxChat Smart Recommender Tutorial', 'mxchat'); ?></h3>
787 + <div class="video-description">
788 + <p><?php echo esc_html__('Learn how to create intelligent recommendation flows that guide users to perfect matches based on their preferences.', 'mxchat'); ?></p>
789 + <a href="https://www.youtube.com/watch?v=8te1KPa238g&t=1s" target="_blank" rel="noopener" class="video-link">
790 + <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>
791 + <?php echo esc_html__('Watch Smart Recommender Tutorial', 'mxchat'); ?>
792 + </a>
556 793 </div>
794 + </div>
557 795
558 - <div class="faq-item">
559 - <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
560 - <p>
561 - <?php echo esc_html__('AI chatbots can sometimes behave in unexpected ways, especially if you\'re new to configuring AI for specific tasks. We\'re committed to helping you get the most out of your chatbot experience. While we’re working on comprehensive guides and video tutorials, our team is here to assist you directly. If your chatbot isn\'t delivering the responses you need, please don’t hesitate to', 'mxchat'); ?> <a href="https://mxchat.ai/contact/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('contact us', 'mxchat'); ?></a> <?php echo esc_html__('for personalized support.', 'mxchat'); ?>
562 - </p>
563 - <p>
564 - <?php echo esc_html__('We’re dedicated to your success and ready to guide you in aligning the AI\'s behavior to meet your goals.', 'mxchat'); ?>
565 - </p>
796 + <div class="tutorial-item">
797 + <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
798 + <div class="video-description">
799 + <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>
800 + <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
801 + <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>
802 + <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
803 + </a>
566 804 </div>
805 + </div>
567 806
568 - <div class="faq-item">
569 - <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
570 - <p>
571 - <?php echo esc_html__('Loops is a powerful SaaS email service that helps you automate and enhance your email marketing campaigns, making it easy to reach and engage with your audience. To integrate Loops with MxChat, you’ll need an API key from Loops. You can obtain this key by logging into your Loops account and navigating to the API settings. Visit the', 'mxchat'); ?> <a href="https://loops.so" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Loops website', 'mxchat'); ?></a> <?php echo esc_html__('to get started or to sign up for an account.', 'mxchat'); ?>
572 - </p>
807 + <div class="tutorial-item">
808 + <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
809 + <div class="video-description">
810 + <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>
811 + <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
812 + <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>
813 + <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
814 + </a>
573 815 </div>
574 -
575 816 </div>
576 - </div>
577 - <?php
578 -}
579 817
580 -
581 -
582 -public function mxchat_create_transcripts_page() {
583 - global $wpdb;
584 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
585 -
586 - // Get basic stats
587 - $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
588 - $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
589 - $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
590 - ?>
591 - <div class="wrap mxchat-admin">
592 - <!-- Stats Cards -->
593 - <div class="mxchat-stats-grid">
594 - <div class="mxchat-stat-card">
595 - <div class="stat-icon">💬</div>
596 - <div class="stat-content">
597 - <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
598 - <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
599 - </div>
818 + <div class="tutorial-item">
819 + <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
820 + <div class="video-description">
821 + <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>
822 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
823 + <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
824 + <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>
825 + <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
826 + </a>
600 827 </div>
601 - <div class="mxchat-stat-card">
602 - <div class="stat-icon">📝</div>
603 - <div class="stat-content">
604 - <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
605 - <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
606 - </div>
607 - </div>
608 - <div class="mxchat-stat-card">
609 - <div class="stat-icon">👥</div>
610 - <div class="stat-content">
611 - <span class="stat-value"><?php echo esc_html($total_users); ?></span>
612 - <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
613 - </div>
614 - </div>
615 828 </div>
616 829
617 - <!-- Search and Filter Controls -->
618 - <div class="mxchat-controls-wrapper">
619 - <div class="mxchat-search-box">
620 - <input type="text" id="mxchat-search-transcripts"
621 - placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
622 - class="regular-text">
830 + <div class="tutorial-item">
831 + <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
832 + <div class="video-description">
833 + <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>
834 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
835 + <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
836 + <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>
837 + <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
838 + </a>
623 839 </div>
840 + </div>
841 + </div>
624 842
625 - <form id="mxchat-delete-form" method="post">
626 - <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
627 - <div class="mxchat-controls">
628 - <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
629 - <span class="dashicons dashicons-yes-alt"></span>
630 - <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
631 - </button>
632 - <button type="submit" class="button delete-chats-button">
633 - <span class="dashicons dashicons-trash"></span>
634 - <?php esc_html_e('Delete Selected', 'mxchat'); ?>
635 - </button>
843 + </div>
636 844 </div>
637 - </form>
845 + </div>
638 846 </div>
639 -
640 - <!-- Transcripts Container -->
641 - <div id="mxchat-transcripts"></div>
642 847 </div>
643 848 <?php
644 849 }
645 850
646 -public function mxchat_create_prompts_page() {
647 - global $wpdb;
648 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
851 +public function dismiss_live_agent_notice() {
852 + // Add debugging
853 + //error_log('dismiss_live_agent_notice called');
854 + //error_log('POST data: ' . print_r($_POST, true));
649 855
650 - // Display success message if all prompts were deleted
651 - if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
652 - echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
856 + // Verify nonce
857 + if (!wp_verify_nonce($_POST['nonce'], 'dismiss_live_agent_notice')) {
858 + //error_log('Nonce verification failed');
859 + wp_die('Security check failed');
653 860 }
654 861
655 - // Set up pagination and search query
656 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
657 - $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
658 - $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
659 - $per_page = 10;
660 - $offset = ($current_page - 1) * $per_page;
862 + // Remove the notice flag
863 + $deleted = delete_option('mxchat_show_live_agent_disabled_notice');
864 + //error_log('Option deleted: ' . ($deleted ? 'yes' : 'no'));
661 865
662 - // Modify query to handle search input
663 - $sql_search = "";
664 - if ($search_query) {
665 - $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
866 + wp_send_json_success();
867 +}
868 +public function add_live_agent_nonce() {
869 + if (get_option('mxchat_show_live_agent_disabled_notice', false)) {
870 + // Make sure your admin script is enqueued and localize the data
871 + wp_localize_script('mxchat-admin-js', 'mxchatLiveAgent', array(
872 + 'nonce' => wp_create_nonce('dismiss_live_agent_notice'),
873 + 'ajaxurl' => admin_url('admin-ajax.php')
874 + ));
666 875 }
876 +}
667 877
668 - // Retrieve total number of prompts, considering search filter
669 - $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
670 - $total_pages = ceil($total_prompts / $per_page);
671 878
672 - // Retrieve prompts from the database
673 - $prompts = $wpdb->get_results(
674 - $wpdb->prepare(
675 - "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
676 - $per_page,
677 - $offset
879 +public function mxchat_create_transcripts_page() {
880 + global $wpdb;
881 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
882 +
883 + // Get basic stats
884 + $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
885 + $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
886 +
887 + // Count unique users with detailed breakdown
888 + $total_users = $wpdb->get_var("
889 + SELECT COUNT(DISTINCT
890 + CASE
891 + WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
892 + WHEN user_id != 0 THEN CONCAT('user_', user_id)
893 + WHEN user_identifier NOT LIKE 'Tech-Savvy User'
894 + AND user_identifier NOT LIKE 'Detail-Oriented User'
895 + AND user_identifier NOT LIKE 'Language Learner'
896 + AND user_identifier NOT LIKE 'Casual Browser'
897 + AND user_identifier NOT LIKE 'Policy Enforcer'
898 + AND user_identifier NOT LIKE 'Researcher'
899 + AND user_identifier NOT LIKE 'Loyalty Member'
900 + AND user_identifier NOT LIKE 'Gift Buyer'
901 + AND user_identifier NOT LIKE 'Parent or Caregiver'
902 + THEN user_identifier
903 + ELSE session_id
904 + END
678 905 )
679 - );
906 + FROM $table_name
907 + WHERE role != 'assistant'
908 + ");
680 909
681 - // Add the pagination links generation here
682 - $page_links = paginate_links(array(
683 - 'base' => add_query_arg(array(
684 - 'paged' => '%#%',
685 - 'search' => urlencode($search_query),
686 - '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
687 - ), admin_url('admin.php?page=mxchat-prompts')),
688 - 'format' => '',
689 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
690 - 'next_text' => __('Next &raquo;', 'mxchat'),
691 - 'total' => $total_pages,
692 - 'current' => $current_page,
693 - ));
910 + // Get user type breakdown
911 + $registered_users = $wpdb->get_var("
912 + SELECT COUNT(DISTINCT user_email)
913 + FROM $table_name
914 + WHERE user_email != '' AND user_email IS NOT NULL
915 + ");
694 916
695 - // Retrieve processing statuses
696 - $pdf_url = get_transient('mxchat_last_pdf_url');
697 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
698 - $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
699 - $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
917 + $guest_users = $wpdb->get_var("
918 + SELECT COUNT(DISTINCT user_identifier)
919 + FROM $table_name
920 + WHERE (user_email = '' OR user_email IS NULL)
921 + AND role != 'assistant'
922 + AND user_identifier NOT LIKE 'Tech-Savvy User'
923 + AND user_identifier NOT LIKE 'Detail-Oriented User'
924 + AND user_identifier NOT LIKE 'Language Learner'
925 + AND user_identifier NOT LIKE 'Casual Browser'
926 + AND user_identifier NOT LIKE 'Policy Enforcer'
927 + AND user_identifier NOT LIKE 'Researcher'
928 + AND user_identifier NOT LIKE 'Loyalty Member'
929 + AND user_identifier NOT LIKE 'Gift Buyer'
930 + AND user_identifier NOT LIKE 'Parent or Caregiver'
931 + ");
700 932
701 - if ($pdf_status && $pdf_status['status'] === 'complete') {
702 - delete_transient('mxchat_last_pdf_url');
703 - $pdf_status = false;
704 - }
705 - if ($sitemap_status && $sitemap_status['status'] === 'complete') {
706 - delete_transient('mxchat_last_sitemap_url');
707 - $sitemap_status = false;
708 - }
709 -
710 - $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
711 - || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
933 + // Get agent test messages count
934 + $agent_tests = $wpdb->get_var("
935 + SELECT COUNT(DISTINCT session_id)
936 + FROM $table_name
937 + WHERE user_identifier IN (
938 + 'Tech-Savvy User',
939 + 'Detail-Oriented User',
940 + 'Language Learner',
941 + 'Casual Browser',
942 + 'Policy Enforcer',
943 + 'Researcher',
944 + 'Loyalty Member',
945 + 'Gift Buyer',
946 + 'Parent or Caregiver'
947 + )
948 + ");
712 949 ?>
713 - <div class="wrap mxchat-wrapper">
950 + <div class="wrap mxchat-transcripts-wrapper">
714 951 <!-- Hero Section -->
715 - <div class="mxchat-hero">
952 + <div class="mxchat-transcripts-hero">
716 953 <h1 class="mxchat-main-title">
717 - <span class="mxchat-gradient-text">Knowledge Base</span> Manager
954 + Chat <span class="mxchat-gradient-text">Transcripts</span>
718 955 </h1>
719 956 <p class="mxchat-hero-subtitle">
720 - <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
957 + <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
721 958 </p>
722 959 </div>
723 -
724 960 <div class="mxchat-content">
725 - <!-- Import Settings Card -->
726 - <div class="mxchat-card">
727 - <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
728 - <p><?php esc_html_e('Using the free Pinecone DB Manager add-on is recommended for high performance and a knowledge database of more than 500.', 'mxchat'); ?></p>
729 -
730 - <!-- Tab Contents -->
731 - <div class="mxchat-tab-contents">
732 - <!-- Default Database Tab -->
733 - <div id="default-db" class="mxchat-tab-content active">
734 - <!-- Auto-Sync Settings -->
735 - <div class="mxchat-settings-section">
736 - <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
737 - <p class="mxchat-description">
738 - <?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'); ?>
739 - </p>
740 - <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
741 - <div class="mxchat-toggle-group">
742 - <div class="mxchat-toggle-container">
743 - <label class="mxchat-toggle-switch">
744 - <input type="checkbox"
745 - name="mxchat_auto_sync_posts"
746 - class="mxchat-autosave-field"
747 - value="1"
748 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
749 - <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
750 - <span class="mxchat-toggle-slider"></span>
751 - </label>
752 - <span class="mxchat-toggle-label">
753 - <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
754 - </span>
755 - </div>
756 - <div class="mxchat-toggle-container">
757 - <label class="mxchat-toggle-switch">
758 - <input type="checkbox"
759 - name="mxchat_auto_sync_pages"
760 - class="mxchat-autosave-field"
761 - value="1"
762 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
763 - <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
764 - <span class="mxchat-toggle-slider"></span>
765 - </label>
766 - <span class="mxchat-toggle-label">
767 - <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
768 - </span>
769 - </div>
770 - </div>
771 - </div>
961 + <!-- Stats Cards -->
962 + <div class="mxchat-stats-grid">
963 + <div class="mxchat-stat-card">
964 + <div class="stat-icon">💬</div>
965 + <div class="stat-content">
966 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
967 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
772 968 </div>
773 -
774 - <!-- Import Methods -->
775 - <div class="mxchat-import-methods">
776 - <div class="mxchat-method-card">
777 - <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
778 - <p><?php esc_html_e('Import all content from your sitemap automatically.', 'mxchat'); ?></p>
779 - </div>
780 - <div class="mxchat-method-card">
781 - <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
782 - <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
783 - </div>
784 - <div class="mxchat-method-card">
785 - <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
786 - <p><?php esc_html_e('Import content from any specific webpage.', 'mxchat'); ?></p>
787 - </div>
788 - <div class="mxchat-method-card">
789 - <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
790 - <p><?php esc_html_e('Directly submit content to be vectorized.', 'mxchat'); ?></p>
791 - </div>
792 - </div>
793 -
794 - <!-- Import Form -->
795 - <?php if (!$is_processing) : ?>
796 - <div class="mxchat-import-form">
797 - <form id="mxchat-sitemap-form" method="post"
798 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
799 - <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
800 - <div class="mxchat-url-input-group">
801 - <input type="url"
802 - name="sitemap_url"
803 - id="sitemap_url"
804 - placeholder="<?php esc_attr_e('Enter Sitemap, PDF, or Webpage URL', 'mxchat'); ?>"
805 - required />
806 - <button type="submit"
807 - name="submit_sitemap"
808 - class="mxchat-button-primary">
809 - <?php esc_html_e('Import', 'mxchat'); ?>
810 - </button>
811 - </div>
812 - </form>
813 - </div>
814 - <?php endif; ?>
815 -
816 - <!-- Processing Status -->
817 - <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
818 - <div class="mxchat-status-card">
819 - <div class="mxchat-status-header">
820 - <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
821 - <?php if ($is_processing) : ?>
822 - <form method="post" class="mxchat-stop-form"
823 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
824 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
825 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
826 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
827 - </button>
828 - </form>
829 - <?php endif; ?>
830 - </div>
831 - <div class="mxchat-progress-bar">
832 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
833 - </div>
834 - <div class="mxchat-status-details">
835 - <p><?php printf(
836 - esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
837 - absint($pdf_status['processed_pages']),
838 - absint($pdf_status['total_pages']),
839 - absint($pdf_status['percentage'])
840 - ); ?></p>
841 - <p><?php printf(
842 - esc_html__('Status: %s', 'mxchat'),
843 - esc_html(ucfirst($pdf_status['status']))
844 - ); ?></p>
845 - <p><?php printf(
846 - esc_html__('Last update: %s', 'mxchat'),
847 - esc_html($pdf_status['last_update'])
848 - ); ?></p>
849 - </div>
850 - </div>
851 - <?php endif; ?>
852 -
853 - <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
854 - <div class="mxchat-status-card">
855 - <div class="mxchat-status-header">
856 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
857 - <?php if ($is_processing) : ?>
858 - <form method="post" class="mxchat-stop-form"
859 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
860 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
861 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
862 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
863 - </button>
864 - </form>
865 - <?php endif; ?>
866 - </div>
867 - <div class="mxchat-progress-bar">
868 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
869 - </div>
870 - <div class="mxchat-status-details">
871 - <p><?php printf(
872 - esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
873 - absint($sitemap_status['processed_urls']),
874 - absint($sitemap_status['total_urls']),
875 - absint($sitemap_status['percentage'])
876 - ); ?></p>
877 - <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
878 - <div class="mxchat-error-notice">
879 - <?php if (!empty($sitemap_status['error'])) : ?>
880 - <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
881 - <?php endif; ?>
882 - <?php if (!empty($sitemap_status['last_error'])) : ?>
883 - <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
884 - <?php endif; ?>
885 - </div>
886 - <?php endif; ?>
887 - </div>
888 - </div>
889 - <?php endif; ?>
969 + </div>
970 + <div class="mxchat-stat-card">
971 + <div class="stat-icon">📝</div>
972 + <div class="stat-content">
973 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
974 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
890 975 </div>
976 + </div>
977 + <div class="mxchat-stat-card">
978 + <div class="stat-icon">👥</div>
979 + <div class="stat-content">
980 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
981 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
982 + <span class="stat-sublabel">
983 + <?php
984 + echo sprintf(
985 + esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
986 + $registered_users,
987 + $guest_users,
988 + $agent_tests
989 + );
990 + ?>
991 + </span>
992 + </div>
993 + </div>
994 + </div>
891 995
892 -
996 + <!-- Search and Filter Controls with Notification Settings Button -->
997 + <div class="mxchat-controls-wrapper">
998 + <div class="mxchat-search-box">
999 + <input type="text" id="mxchat-search-transcripts"
1000 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
1001 + class="regular-text">
893 1002 </div>
1003 + <form id="mxchat-delete-form" method="post">
1004 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
1005 + <div class="mxchat-controls">
1006 + <button type="button" id="mxchat-chat-email-notification-btn" class="mxchat-action-button">
1007 + <span class="dashicons dashicons-email"></span>
1008 + <?php esc_html_e('Notification Settings', 'mxchat'); ?>
1009 + </button>
1010 + <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
1011 + <span class="dashicons dashicons-download"></span>
1012 + <?php esc_html_e('Export All Chats', 'mxchat'); ?>
1013 + </button>
1014 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
1015 + <span class="dashicons dashicons-yes-alt"></span>
1016 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
1017 + </button>
1018 + <button type="submit" class="button delete-chats-button">
1019 + <span class="dashicons dashicons-trash"></span>
1020 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
1021 + </button>
1022 + </div>
1023 + </form>
894 1024 </div>
895 1025
896 - <!-- Direct Content Submission Card -->
897 -<div class="mxchat-card">
898 - <div class="mxchat-card-header">
899 - <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
1026 + <!-- Transcripts Container -->
1027 + <div id="mxchat-transcripts"></div>
1028 + </div>
900 1029 </div>
901 - <form id="mxchat-content-form" method="post"
902 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
903 - <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
904 - <div class="mxchat-form-group">
905 - <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
906 - <textarea name="article_content" id="article_content" required
907 - placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
908 - </div>
909 - <div class="mxchat-form-group">
910 - <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
911 - <input type="url" name="article_url" id="article_url"
912 - placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
913 - </div>
914 - <button type="submit" name="submit_content" class="mxchat-button-primary">
915 - <?php esc_html_e('Submit Content', 'mxchat'); ?>
916 - </button>
917 - </form>
918 -</div>
919 1030
920 -<!-- Knowledge Base Table Card -->
921 -<div class="mxchat-card">
922 - <div class="mxchat-card-header">
923 - <h2><?php esc_html_e('Knowledge Base', 'mxchat'); ?></h2>
924 - <div class="mxchat-header-actions">
925 - <!-- Search -->
926 - <form method="get" id="knowledge-search" class="mxchat-search-form">
927 - <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
928 - <input type="hidden" name="page" value="mxchat-prompts" />
929 - <div class="mxchat-search-group">
930 - <span class="dashicons dashicons-search"></span>
931 - <input type="text"
932 - name="search"
933 - placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
934 - value="<?php echo esc_attr($search_query); ?>" />
935 - </div>
936 - </form>
937 -
938 - <!-- Delete All -->
939 - <form method="post"
940 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
941 - class="mxchat-delete-form"
942 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
943 - <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
944 - <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
945 - <span class="dashicons dashicons-trash"></span>
946 - <?php esc_html_e('Delete All', 'mxchat'); ?>
947 - </button>
948 - </form>
1031 + <!-- Modal for Chat Email Notification Settings -->
1032 + <div id="mxchat-chat-email-notification-modal" class="mxchat-chat-notification-modal-overlay" style="display: none;">
1033 + <div class="mxchat-chat-notification-modal-content">
1034 + <div class="mxchat-chat-notification-modal-header">
1035 + <h2><?php esc_html_e('Email Notification Settings', 'mxchat'); ?></h2>
1036 + <button type="button" class="mxchat-chat-notification-modal-close">&times;</button>
1037 + </div>
1038 + <div class="mxchat-chat-notification-modal-body">
1039 + <form method="post" action="options.php" id="mxchat-chat-email-notification-form">
1040 + <?php
1041 + settings_fields('mxchat_transcripts_options');
1042 + do_settings_sections('mxchat-transcripts');
1043 + ?>
1044 + <div class="mxchat-chat-notification-modal-footer">
1045 + <button type="submit" class="button button-primary">
1046 + <?php esc_html_e('Save Notification Settings', 'mxchat'); ?>
1047 + </button>
1048 + <button type="button" class="button mxchat-chat-notification-modal-cancel">
1049 + <?php esc_html_e('Cancel', 'mxchat'); ?>
1050 + </button>
1051 + </div>
1052 + </form>
1053 + </div>
949 1054 </div>
950 1055 </div>
1056 + <?php
1057 +}
951 1058
952 - <!-- Table -->
953 - <div class="mxchat-table-wrapper">
954 - <table class="mxchat-records-table">
955 - <thead>
956 - <tr>
957 - <th><?php esc_html_e('ID', 'mxchat'); ?></th>
958 - <th><?php esc_html_e('Content', 'mxchat'); ?></th>
959 - <th><?php esc_html_e('Source', 'mxchat'); ?></th>
960 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
961 - </tr>
962 - </thead>
963 - <tbody>
964 - <?php if ($prompts) : ?>
965 - <?php foreach ($prompts as $prompt) : ?>
966 - <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
967 - <td><?php echo esc_html($prompt->id); ?></td>
968 - <td class="mxchat-content-cell">
969 - <div class="content-view">
970 - <?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?>
971 - </div>
972 - <textarea class="content-edit" style="display:none;">
973 - <?php echo esc_textarea($prompt->article_content); ?>
974 - </textarea>
975 - </td>
976 - <td class="mxchat-url-cell">
977 - <?php if (!empty($prompt->source_url)) : ?>
978 - <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
979 - <span class="dashicons dashicons-external"></span>
980 - <?php esc_html_e('View Source', 'mxchat'); ?>
981 - </a>
982 - <?php else : ?>
983 - <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
984 - <?php endif; ?>
985 - </td>
986 - <td class="mxchat-actions-cell">
987 - <button class="mxchat-button-icon edit-button"
988 - data-id="<?php echo esc_attr($prompt->id); ?>">
989 - <span class="dashicons dashicons-edit"></span>
990 - </button>
991 - <button class="mxchat-button-icon save-button"
992 - data-id="<?php echo esc_attr($prompt->id); ?>"
993 - style="display:none;"
994 - data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
995 - <span class="dashicons dashicons-saved"></span>
996 - </button>
997 - <a href="<?php echo esc_url(admin_url(
998 - 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
999 - . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1000 - )); ?>"
1001 - class="mxchat-button-icon delete-button"
1002 - onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1003 - <span class="dashicons dashicons-trash"></span>
1004 - </a>
1005 - </td>
1006 - </tr>
1007 - <?php endforeach; ?>
1008 - <?php else : ?>
1009 - <tr>
1010 - <td colspan="4" class="mxchat-no-records">
1011 - <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1012 - </td>
1013 - </tr>
1014 - <?php endif; ?>
1015 - </tbody>
1016 - </table>
1017 - </div>
1018 1059
1019 - <?php if ($page_links) : ?>
1020 - <div class="mxchat-pagination">
1021 - <?php echo wp_kses_post($page_links); ?>
1022 - </div>
1023 - <?php endif; ?>
1024 -</div>
1025 -</div>
1026 -</div>
1027 -<?php
1060 +public function mxchat_transcripts_notification_section_callback() {
1061 + 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>';
1028 1062 }
1063 +public function mxchat_enable_notifications_callback() {
1064 + $options = get_option('mxchat_transcripts_options', array());
1065 + $enabled = isset($options['mxchat_enable_notifications']) ? $options['mxchat_enable_notifications'] : 0;
1066 + ?>
1067 + <label for="mxchat_enable_notifications">
1068 + <input type="checkbox" id="mxchat_enable_notifications"
1069 + name="mxchat_transcripts_options[mxchat_enable_notifications]"
1070 + value="1" <?php checked(1, $enabled); ?>>
1071 + <?php esc_html_e('Send email notification when a new chat session starts', 'mxchat'); ?>
1072 + </label>
1073 + <p class="description">
1074 + <?php esc_html_e('Enable this option to receive email notifications for new chat sessions.', 'mxchat'); ?>
1075 + </p>
1076 + <?php
1077 +}
1078 +public function mxchat_notification_email_callback() {
1079 + $options = get_option('mxchat_transcripts_options', array());
1080 + $email = isset($options['mxchat_notification_email']) ? $options['mxchat_notification_email'] : get_option('admin_email');
1081 + ?>
1082 + <input type="email" id="mxchat_notification_email"
1083 + name="mxchat_transcripts_options[mxchat_notification_email]"
1084 + value="<?php echo esc_attr($email); ?>"
1085 + class="regular-text">
1086 + <p class="description">
1087 + <?php esc_html_e('Enter the email address where notifications should be sent. Defaults to the admin email address.', 'mxchat'); ?>
1088 + </p>
1089 + <?php
1090 +}
1091 +public function sanitize_transcripts_options($input) {
1092 + $sanitized = array();
1029 1093
1094 + $sanitized['mxchat_enable_notifications'] = isset($input['mxchat_enable_notifications']) ? 1 : 0;
1030 1095
1031 -public function mxchat_handle_delete_all_prompts() {
1032 - // Verify nonce
1033 - if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
1034 - wp_die(__('Nonce verification failed.', 'mxchat'));
1096 + if (isset($input['mxchat_notification_email'])) {
1097 + $sanitized['mxchat_notification_email'] = sanitize_email($input['mxchat_notification_email']);
1098 + if (!is_email($sanitized['mxchat_notification_email'])) {
1099 + add_settings_error(
1100 + 'mxchat_transcripts_options',
1101 + 'invalid_email',
1102 + __('Please enter a valid email address for notifications.', 'mxchat'),
1103 + 'error'
1104 + );
1105 + $sanitized['mxchat_notification_email'] = get_option('admin_email');
1106 + }
1035 1107 }
1036 1108
1037 - // Check permissions
1109 + return $sanitized;
1110 +}
1111 +public function export_chat_transcripts() {
1038 1112 if (!current_user_can('manage_options')) {
1039 - wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
1113 + wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
1040 1114 }
1041 1115
1116 + check_ajax_referer('mxchat_export_transcripts', 'security');
1117 +
1042 1118 global $wpdb;
1043 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1119 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1044 1120
1045 - // Delete all prompts from the table
1046 - $wpdb->query("DELETE FROM {$table_name}");
1121 + // Get all transcripts ordered by session and timestamp
1122 + $results = $wpdb->get_results(
1123 + "SELECT session_id, user_email, user_identifier, role, message, timestamp
1124 + FROM {$table_name}
1125 + ORDER BY session_id, timestamp ASC"
1126 + );
1047 1127
1048 - // Clear relevant cache
1049 - wp_cache_delete('all_prompts', 'mxchat_prompts');
1128 + if (empty($results)) {
1129 + wp_send_json_error(array('message' => 'No transcripts found.'));
1130 + wp_die();
1131 + }
1050 1132
1051 - // Redirect back with a success message
1052 - $redirect_url = add_query_arg(array(
1053 - 'page' => 'mxchat-prompts',
1054 - 'all_deleted' => 'true'
1055 - ), admin_url('admin.php'));
1133 + // Set headers for CSV download
1134 + header('Content-Type: text/csv');
1135 + header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
1136 + header('Pragma: no-cache');
1137 + header('Expires: 0');
1056 1138
1057 - wp_safe_redirect($redirect_url);
1058 - exit;
1059 -}
1060 -public function mxchat_handle_delete_prompt() {
1061 - // Sanitize and validate nonce
1062 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
1063 - if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
1064 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1065 - }
1139 + // Create output stream
1140 + $output = fopen('php://output', 'w');
1066 1141
1067 - // Check permissions
1068 - if (!current_user_can('manage_options')) {
1069 - wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
1070 - }
1142 + // Add UTF-8 BOM for proper Excel encoding
1143 + fputs($output, "\xEF\xBB\xBF");
1071 1144
1072 - // Validate and sanitize ID parameter
1073 - $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
1074 - if ($id <= 0) {
1075 - wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
1145 + // Add CSV headers
1146 + fputcsv($output, array(
1147 + 'Session ID',
1148 + 'Email',
1149 + 'User Identifier',
1150 + 'Role',
1151 + 'Message',
1152 + 'Timestamp'
1153 + ));
1154 +
1155 + // Add data rows
1156 + foreach ($results as $row) {
1157 + fputcsv($output, array(
1158 + $row->session_id,
1159 + $row->user_email,
1160 + $row->user_identifier,
1161 + $row->role,
1162 + $row->message,
1163 + $row->timestamp
1164 + ));
1076 1165 }
1077 1166
1078 - global $wpdb;
1079 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1080 -
1081 - // Clear cache and delete prompt
1082 - wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
1083 - $wpdb->delete($table_name, array('id' => $id), array('%d'));
1084 -
1085 - wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1086 - exit;
1167 + fclose($output);
1168 + wp_die();
1087 1169 }
1088 -public function mxchat_generate_embedding($text) {
1089 - $options = get_option('mxchat_options');
1090 - $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1091 1170
1092 - $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1093 - 'body' => wp_json_encode(array(
1094 - 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1095 - 'input' => $text
1096 - )),
1097 - 'headers' => array(
1098 - 'Authorization' => 'Bearer ' . $api_key,
1099 - 'Content-Type' => esc_html__('application/json', 'mxchat')
1100 - ),
1101 - ));
1102 1171
1103 - if (is_wp_error($response)) {
1104 - return null;
1105 - }
1172 +public function mxchat_fetch_chat_history() {
1173 + global $wpdb;
1174 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1175 + $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1106 1176
1107 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1108 - return $response_data['data'][0]['embedding'] ?? null;
1177 + if (!current_user_can('manage_options')) {
1178 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1109 1179 }
1110 -public function mxchat_delete_chat_history() {
1111 - if (!current_user_can('manage_options')) {
1112 - echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1113 - wp_die();
1114 - }
1115 1180
1116 - check_ajax_referer('mxchat_delete_chat_history', 'security');
1181 + $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1182 + $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
1183 + $offset = ($page - 1) * $per_page;
1184 + $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
1117 1185
1118 - global $wpdb;
1119 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1186 + $search_condition = '';
1187 + $search_params = [];
1120 1188
1121 - if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
1122 - foreach ($_POST['delete_session_ids'] as $session_id) {
1123 - $session_id_sanitized = sanitize_text_field($session_id);
1189 + if (!empty($search)) {
1190 + $search_condition = "WHERE (
1191 + session_id LIKE %s
1192 + OR user_email LIKE %s
1193 + OR user_name LIKE %s
1194 + OR user_identifier LIKE %s
1195 + OR message LIKE %s
1196 + )";
1197 + $search_params = array_fill(0, 5, '%' . $wpdb->esc_like($search) . '%');
1198 + }
1124 1199
1125 - // Clear relevant cache before deletion
1126 - $cache_key = 'chat_session_' . $session_id_sanitized;
1127 - wp_cache_delete($cache_key, 'mxchat_chat_sessions');
1200 + $count_query = !empty($search)
1201 + ? $wpdb->prepare("SELECT COUNT(DISTINCT session_id) FROM {$table_name} {$search_condition}", $search_params)
1202 + : "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
1128 1203
1129 - // Perform the deletion
1130 - $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
1131 - }
1204 + $total_sessions = $wpdb->get_var($count_query);
1132 1205
1133 - // Optionally, clear a general cache if you have one
1134 - wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
1206 + $session_query = !empty($search)
1207 + ? $wpdb->prepare(
1208 + "SELECT DISTINCT t.session_id FROM {$table_name} t {$search_condition}
1209 + GROUP BY t.session_id ORDER BY MAX(t.timestamp) DESC LIMIT %d OFFSET %d",
1210 + array_merge($search_params, [$per_page, $offset])
1211 + )
1212 + : $wpdb->prepare(
1213 + "SELECT DISTINCT session_id FROM {$table_name}
1214 + GROUP BY session_id ORDER BY MAX(timestamp) DESC LIMIT %d OFFSET %d",
1215 + $per_page, $offset
1216 + );
1135 1217
1136 - echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]);
1137 - } else {
1138 - echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
1218 + $session_ids = $wpdb->get_col($session_query);
1219 +
1220 + if (empty($session_ids)) {
1221 + ob_start();
1222 + echo '<div class="mxchat-no-results">';
1223 + echo esc_html__('No chat history found.', 'mxchat');
1224 + if (!empty($search)) {
1225 + echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
1139 1226 }
1140 -
1227 + echo '</div>';
1228 + wp_send_json([
1229 + 'html' => ob_get_clean(),
1230 + 'page' => $page,
1231 + 'total_pages' => 0,
1232 + 'total_sessions' => 0
1233 + ]);
1141 1234 wp_die();
1142 1235 }
1143 1236
1237 + $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
1238 + $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
1144 1239
1145 -public function mxchat_save_inline_prompt() {
1146 - // Check for nonce security
1147 - check_ajax_referer('mxchat_save_inline_nonce');
1240 + ob_start();
1241 + echo '<div class="mxchat-transcript">';
1148 1242
1149 - // Verify permissions
1150 - if (!current_user_can('manage_options')) {
1151 - wp_send_json_error(esc_html__('Permission denied.', 'mxchat'));
1152 - return;
1153 - }
1243 + foreach ($session_ids as $session_id) {
1244 + $session_data = $originating_columns_exist
1245 + ? $wpdb->get_row($wpdb->prepare(
1246 + "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
1247 + FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
1248 + $session_id
1249 + ))
1250 + : $wpdb->get_row($wpdb->prepare(
1251 + "SELECT user_email, user_name, user_identifier, timestamp FROM {$table_name}
1252 + WHERE session_id = %s LIMIT 1",
1253 + $session_id
1254 + ));
1154 1255
1155 - global $wpdb;
1156 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1256 + $clicked_urls = [];
1257 + if ($url_table_exists) {
1258 + $url_clicks = $wpdb->get_results(
1259 + $wpdb->prepare(
1260 + "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
1261 + WHERE session_id = %s ORDER BY click_timestamp ASC",
1262 + $session_id
1263 + )
1264 + );
1265 + foreach ($url_clicks as $click) {
1266 + $clicked_urls[] = $click->clicked_url;
1267 + }
1268 + }
1157 1269
1158 - // Validate and sanitize input data
1159 - $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1160 - $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1161 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1270 + $messages = $wpdb->get_results(
1271 + $wpdb->prepare(
1272 + "SELECT * FROM {$table_name}
1273 + WHERE session_id = %s ORDER BY timestamp ASC",
1274 + $session_id
1275 + )
1276 + );
1162 1277
1163 - if ($prompt_id > 0 && !empty($article_content)) {
1164 - // Re-generate the embedding vector for the updated content
1165 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1278 + $latest_timestamp = !empty($messages) ? end($messages)->timestamp : $session_data->timestamp;
1279 + $session_time = wp_date('M j, g:ia', strtotime($latest_timestamp . ' UTC'));
1166 1280
1167 - if (is_array($embedding_vector)) {
1168 - // Serialize the embedding vector before storing it
1169 - $embedding_vector_serialized = serialize($embedding_vector);
1281 + $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
1282 + $user_name = !empty($session_data->user_name) ? $session_data->user_name : ''; // NEW
1283 + $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
1170 1284
1171 - // Update the prompt in the database
1172 - $updated = $wpdb->update(
1173 - $table_name,
1174 - array(
1175 - 'article_content' => $article_content,
1176 - 'embedding_vector' => $embedding_vector_serialized,
1177 - 'source_url' => $article_url,
1178 - ),
1179 - array('id' => $prompt_id),
1180 - array('%s', '%s', '%s'),
1181 - array('%d')
1182 - );
1183 -
1184 - if ($updated !== false) {
1185 - wp_send_json_success();
1186 - } else {
1187 - wp_send_json_error(esc_html__('Database update failed.', 'mxchat'));
1285 + // NEW: Build user display with name if available
1286 + if (!empty($user_email)) {
1287 + $user_display = $user_email;
1288 + if (!empty($user_name)) {
1289 + $user_display .= ' (' . $user_name . ')';
1188 1290 }
1189 1291 } else {
1190 - wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat'));
1292 + $user_display = $user_identifier;
1191 1293 }
1192 - } else {
1193 - wp_send_json_error(esc_html__('Invalid data.', 'mxchat'));
1194 - }
1195 -}
1294 + echo '<div class="mxchat-session">';
1295 + echo '<div class="mxchat-session-header" data-session-id="' . esc_attr($session_id) . '">';
1296 + echo '<div class="mxchat-user-row">' . esc_html($user_display) . '</div>';
1196 1297
1298 + if ($originating_columns_exist && !empty($session_data->originating_page_url)) {
1299 + $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : $session_data->originating_page_url;
1197 1300
1301 + echo '<div class="mxchat-header-row flex-between">';
1302 + echo '<div class="mxchat-main-info">';
1303 + echo '<a href="' . esc_url($session_data->originating_page_url) . '" target="_blank" class="mxchat-page-link">';
1304 + echo esc_html($page_title);
1305 + echo '</a>';
1306 + echo '<span class="mxchat-session-time">' . esc_html($session_time) . '</span>';
1307 + echo '</div>';
1308 + echo '</div>';
1309 + }
1198 1310
1199 -// Add this method to handle post updates
1200 -public function handle_post_update($post_id, $post, $update) {
1201 - // Basic validation checks
1202 - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1203 - return;
1204 - }
1311 + if (!empty($clicked_urls)) {
1312 + echo '<div class="mxchat-links-row">';
1313 + echo '<span class="mxchat-label">Clicked:</span> ';
1314 + $link_count = 0;
1315 + foreach ($clicked_urls as $url) {
1316 + if ($link_count > 0) echo ', ';
1317 + echo '<a href="' . esc_url($url) . '" target="_blank" class="mxchat-link-item">' . esc_html(parse_url($url, PHP_URL_HOST)) . '</a>';
1318 + $link_count++;
1319 + if ($link_count >= 3) {
1320 + if (count($clicked_urls) > 3) {
1321 + echo ' +' . (count($clicked_urls) - 3) . ' more';
1322 + }
1323 + break;
1324 + }
1325 + }
1326 + echo '</div>';
1327 + }
1205 1328
1206 - // Only process published content
1207 - if (!in_array($post->post_status, array('publish'))) {
1208 - return;
1209 - }
1329 + echo '</div>'; // end session-header
1330 + echo '<div class="mxchat-messages">';
1210 1331
1211 - // Check if sync is enabled for this post type
1212 - $post_type = $post->post_type;
1213 - if (!in_array($post_type, array('post', 'page'))) {
1214 - return;
1215 - }
1332 + foreach ($messages as $transcript) {
1333 + $formatted_timestamp = wp_date('F j, Y g:i a', strtotime($transcript->timestamp . ' UTC'));
1216 1334
1217 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1218 - if (get_option($sync_option) != '1') {
1219 - return;
1220 - }
1335 + switch ($transcript->role) {
1336 + case 'assistant':
1337 + case 'bot':
1338 + $message_class = 'bot-message';
1339 + $display_role = esc_html__('Chatbot', 'mxchat');
1340 + break;
1341 + case 'user':
1342 + $message_class = 'user-message';
1343 + $display_role = !empty($transcript->user_identifier)
1344 + ? sanitize_text_field($transcript->user_identifier)
1345 + : esc_html__('User', 'mxchat');
1346 + break;
1347 + case 'agent':
1348 + $message_class = 'agent-message';
1349 + $display_role = esc_html__('Agent', 'mxchat');
1350 + break;
1351 + default:
1352 + $message_class = 'unknown-message';
1353 + $display_role = esc_html__('Unknown', 'mxchat');
1354 + }
1221 1355
1222 - // Prepare content and URL
1223 - $content = wp_strip_all_tags($post->post_content);
1224 - $url = get_permalink($post_id);
1356 + $message_content = wp_kses(
1357 + stripslashes($transcript->message),
1358 + [
1359 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1360 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1361 + 'a' => ['href' => [], 'title' => [], 'target' => []]
1362 + ]
1363 + );
1364 + $message_content = nl2br($message_content);
1225 1365
1226 - // Generate embedding vector
1227 - $embedding_vector = $this->mxchat_generate_embedding($content);
1228 - if (!$embedding_vector) {
1229 - return;
1366 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1367 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1368 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1369 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1370 + echo '</div>';
1371 + }
1372 +
1373 + echo '</div></div>'; // end messages and session
1230 1374 }
1231 1375
1232 - // Check for Pinecone addon and its settings
1233 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1234 - $use_pinecone = false;
1376 + echo '</div>'; // end transcript
1235 1377
1236 - if ($pinecone_settings && is_array($pinecone_settings)) {
1237 - // Check if Pinecone is enabled and all required settings are present
1238 - $use_pinecone = (
1239 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1240 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1241 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1242 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1243 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1244 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1245 - );
1246 - }
1378 + $total_pages = ceil($total_sessions / $per_page);
1247 1379
1248 - if ($use_pinecone) {
1249 - // Use Pinecone
1250 - $pinecone_result = $this->store_in_pinecone_main(
1251 - $embedding_vector,
1252 - $content,
1253 - $url,
1254 - $pinecone_settings['mxchat_pinecone_api_key'],
1255 - $pinecone_settings['mxchat_pinecone_environment'],
1256 - $pinecone_settings['mxchat_pinecone_index']
1257 - );
1380 + echo '<div class="mxchat-pagination">';
1381 + echo '<div class="mxchat-pagination-info">';
1382 + echo sprintf(
1383 + esc_html__('Showing %1$d to %2$d of %3$d sessions', 'mxchat'),
1384 + $offset + 1,
1385 + min($offset + $per_page, $total_sessions),
1386 + $total_sessions
1387 + );
1388 + echo '</div>';
1258 1389
1259 - if (!$pinecone_result['success']) {
1260 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1261 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1390 + if ($total_pages > 1) {
1391 + echo '<div class="mxchat-pagination-controls">';
1392 + if ($page > 1) {
1393 + echo '<button class="mxchat-pagination-button" data-page="' . ($page - 1) . '">&laquo; ' . esc_html__('Previous', 'mxchat') . '</button>';
1262 1394 }
1263 - } else {
1264 - // Fallback to WordPress DB storage
1265 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1266 - }
1267 -}
1268 -// Modified storage function to add type field
1269 -private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1270 - $vector_id = $vector_id ?: md5($url);
1271 - $options = get_option('mxchat_pinecone_addon_options');
1272 - $host = $options['mxchat_pinecone_host'] ?? '';
1273 1395
1274 - if (empty($host)) {
1275 - return array(
1276 - 'success' => false,
1277 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1278 - );
1279 - }
1396 + $start_page = max(1, $page - 2);
1397 + $end_page = min($total_pages, $page + 2);
1280 1398
1281 - // Determine if this is a product URL
1282 - $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1399 + if ($start_page > 1) {
1400 + echo '<button class="mxchat-pagination-button" data-page="1">1</button>';
1401 + if ($start_page > 2) {
1402 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1403 + }
1404 + }
1283 1405
1284 - $api_endpoint = "https://{$host}/vectors/upsert";
1285 - $request_body = array(
1286 - 'vectors' => array(
1287 - array(
1288 - 'id' => $vector_id,
1289 - 'values' => $embedding_vector,
1290 - 'metadata' => array(
1291 - 'text' => $content,
1292 - 'source_url' => $url,
1293 - 'type' => $is_product ? 'product' : 'content',
1294 - 'last_updated' => time()
1295 - )
1296 - )
1297 - )
1298 - );
1406 + for ($i = $start_page; $i <= $end_page; $i++) {
1407 + $class = $i === $page ? 'mxchat-pagination-button active' : 'mxchat-pagination-button';
1408 + echo '<button class="' . $class . '" data-page="' . $i . '">' . $i . '</button>';
1409 + }
1299 1410
1300 - $response = wp_remote_post($api_endpoint, array(
1301 - 'headers' => array(
1302 - 'Api-Key' => $api_key,
1303 - 'accept' => 'application/json',
1304 - 'content-type' => 'application/json'
1305 - ),
1306 - 'body' => wp_json_encode($request_body),
1307 - 'timeout' => 30,
1308 - 'data_format' => 'body'
1309 - ));
1411 + if ($end_page < $total_pages) {
1412 + if ($end_page < $total_pages - 1) {
1413 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1414 + }
1415 + echo '<button class="mxchat-pagination-button" data-page="' . $total_pages . '">' . $total_pages . '</button>';
1416 + }
1310 1417
1311 - if (is_wp_error($response)) {
1312 - return array(
1313 - 'success' => false,
1314 - 'message' => $response->get_error_message()
1315 - );
1418 + if ($page < $total_pages) {
1419 + echo '<button class="mxchat-pagination-button" data-page="' . ($page + 1) . '">' . esc_html__('Next', 'mxchat') . ' &raquo;</button>';
1420 + }
1421 +
1422 + echo '</div>';
1316 1423 }
1317 1424
1318 - $response_code = wp_remote_retrieve_response_code($response);
1319 - if ($response_code !== 200) {
1320 - $body = wp_remote_retrieve_body($response);
1321 - return array(
1322 - 'success' => false,
1323 - 'message' => sprintf(
1324 - 'Pinecone API error (HTTP %d): %s',
1325 - $response_code,
1326 - $body
1327 - )
1328 - );
1329 - }
1425 + echo '</div>';
1330 1426
1331 - return array(
1332 - 'success' => true,
1333 - 'message' => 'Successfully stored in Pinecone'
1334 - );
1427 + wp_send_json([
1428 + 'html' => ob_get_clean(),
1429 + 'page' => $page,
1430 + 'total_pages' => $total_pages,
1431 + 'total_sessions' => $total_sessions
1432 + ]);
1433 +
1434 + wp_die();
1335 1435 }
1336 -// Helper method for WordPress DB storage
1337 -private function store_in_wordpress_db($content, $url, $embedding_vector) {
1338 - global $wpdb;
1339 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1340 1436
1341 - $existing_entry = $wpdb->get_row(
1342 - $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url)
1343 - );
1344 1437
1345 - if ($existing_entry) {
1346 - $wpdb->update(
1347 - $table_name,
1348 - array(
1349 - 'article_content' => $content,
1350 - 'embedding_vector' => serialize($embedding_vector),
1351 - 'timestamp' => current_time('mysql')
1352 - ),
1353 - array('id' => $existing_entry->id),
1354 - array('%s', '%s', '%s')
1438 +/**
1439 + * ALTERNATIVE: Simpler helper method using string replacement
1440 + */
1441 +private function highlight_clicked_links($message_content, $clicked_urls) {
1442 + if (empty($clicked_urls)) {
1443 + return wp_kses(
1444 + $message_content,
1445 + [
1446 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1447 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1448 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1449 + ]
1355 1450 );
1356 - } else {
1357 - $wpdb->insert(
1358 - $table_name,
1359 - array(
1360 - 'article_content' => $content,
1361 - 'source_url' => $url,
1362 - 'embedding_vector' => serialize($embedding_vector),
1363 - 'timestamp' => current_time('mysql')
1364 - ),
1365 - array('%s', '%s', '%s', '%s')
1366 - );
1367 1451 }
1368 -}
1369 -/**
1370 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1371 - *
1372 - * @param int $post_id The ID of the post being deleted
1373 - * @return void
1374 - */
1375 -public function mxchat_handle_post_delete($post_id) {
1376 - // Get post data before it's deleted
1377 - $post = get_post($post_id);
1378 1452
1379 - // Basic validation
1380 - if (!$post || wp_is_post_revision($post_id)) {
1381 - return;
1382 - }
1453 + // First apply standard sanitization
1454 + $message_content = wp_kses(
1455 + $message_content,
1456 + [
1457 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1458 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1459 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1460 + ]
1461 + );
1383 1462
1384 - // Check post type
1385 - $post_type = $post->post_type;
1386 - if (!in_array($post_type, array('post', 'page'))) {
1387 - return;
1388 - }
1463 + // Process each clicked URL
1464 + foreach ($clicked_urls as $clicked_url) {
1465 + // Try multiple patterns to catch different link formats
1466 + $patterns = [
1467 + // Standard link format
1468 + '/<a([^>]*href=["\']' . preg_quote($clicked_url, '/') . '["\'][^>]*)>/i',
1469 + // Link with trailing slash
1470 + '/<a([^>]*href=["\']' . preg_quote(rtrim($clicked_url, '/'), '/') . '\/?["\'][^>]*)>/i',
1471 + // Encoded entities version
1472 + '/<a([^>]*href=["\']' . preg_quote(htmlentities($clicked_url), '/') . '["\'][^>]*)>/i',
1473 + ];
1389 1474
1390 - // Check if sync is enabled for this post type
1391 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1392 - if (get_option($sync_option) != '1') {
1393 - return;
1394 - }
1475 + foreach ($patterns as $pattern) {
1476 + if (preg_match($pattern, $message_content)) {
1477 + $message_content = preg_replace_callback(
1478 + $pattern,
1479 + function($matches) {
1480 + $full_match = $matches[0];
1481 + $attributes = $matches[1];
1395 1482
1396 - // Get the URL before post is deleted
1397 - $source_url = get_permalink($post_id);
1398 - if (!$source_url) {
1399 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1400 - return;
1401 - }
1483 + // Check if it already has the class
1484 + if (strpos($full_match, 'mxchat-clicked-link') !== false) {
1485 + return $full_match;
1486 + }
1402 1487
1403 - // Check for Pinecone addon and its settings
1404 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1405 - $use_pinecone = false;
1406 - if ($pinecone_settings && is_array($pinecone_settings)) {
1407 - $use_pinecone = (
1408 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1409 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1410 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1411 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1412 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1413 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1414 - );
1415 - }
1488 + // Check if class attribute exists
1489 + if (preg_match('/class=["\']([^"\']*)["\']/', $attributes, $class_matches)) {
1490 + // Add to existing class
1491 + $new_attributes = preg_replace(
1492 + '/class=["\']([^"\']*)["\']/',
1493 + 'class="$1 mxchat-clicked-link"',
1494 + $attributes
1495 + );
1496 + } else {
1497 + // Add new class attribute
1498 + $new_attributes = $attributes . ' class="mxchat-clicked-link"';
1499 + }
1416 1500
1417 - $deletion_successful = false;
1501 + // Add title if not present
1502 + if (strpos($new_attributes, 'title=') === false) {
1503 + $new_attributes .= ' title="User clicked this link"';
1504 + }
1418 1505
1419 - if ($use_pinecone) {
1420 - try {
1421 - $pinecone_result = $this->delete_from_pinecone(
1422 - array($source_url),
1423 - $pinecone_settings['mxchat_pinecone_api_key'],
1424 - $pinecone_settings['mxchat_pinecone_environment'],
1425 - $pinecone_settings['mxchat_pinecone_index']
1426 - );
1506 + return '<a' . $new_attributes . '>';
1507 + },
1508 + $message_content
1509 + );
1427 1510
1428 - if (!$pinecone_result['success']) {
1429 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1430 - } else {
1431 - $deletion_successful = true;
1511 + // If we found and replaced, break out of the patterns loop
1512 + break;
1432 1513 }
1433 - } catch (Exception $e) {
1434 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1435 1514 }
1436 1515 }
1437 1516
1438 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1439 - try {
1440 - global $wpdb;
1441 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1442 -
1443 - $result = $wpdb->delete(
1444 - $table_name,
1445 - array('source_url' => $source_url),
1446 - array('%s')
1447 - );
1448 -
1449 - if ($result === false) {
1450 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1451 - } else {
1452 - $deletion_successful = true;
1453 - }
1454 - } catch (Exception $e) {
1455 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1456 - }
1457 -
1458 - if (!$deletion_successful) {
1459 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1460 - }
1517 + return $message_content;
1461 1518 }
1462 1519
1520 +public function mxchat_create_prompts_page() {
1521 + //error_log('=== DEBUG: mxchat_create_prompts_page started ===');
1463 1522
1464 -public function mxchat_handle_content_submission() {
1465 - // Check if the form was submitted and the user has permission.
1466 - if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1467 - return;
1468 - }
1469 -
1470 - // Verify the nonce.
1471 - $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1472 - if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1473 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1474 - }
1475 -
1476 - // Sanitize the inputs.
1477 - $article_content = sanitize_textarea_field($_POST['article_content']);
1478 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1479 -
1480 - // Generate the embedding vector.
1481 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1482 - if (!is_array($embedding_vector)) {
1483 - set_transient('mxchat_admin_notice_error',
1484 - esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1485 - 30
1486 - );
1487 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1488 - exit;
1489 - }
1490 -
1491 - // Store in the WordPress database.
1492 1523 global $wpdb;
1493 1524 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1494 1525
1495 - // Ensure the source_url column exists.
1496 - if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1497 - $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1498 - }
1526 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1527 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
1499 1528
1500 - // Serialize and store the embedding vector.
1501 - $embedding_vector_serialized = serialize($embedding_vector);
1502 - $inserted = $wpdb->insert(
1503 - $table_name,
1504 - array(
1505 - 'article_content' => $article_content,
1506 - 'embedding_vector' => $embedding_vector_serialized,
1507 - 'source_url' => $article_url,
1508 - ),
1509 - array('%s', '%s', '%s')
1510 - );
1511 -
1512 - if ($inserted === false) {
1513 - set_transient('mxchat_admin_notice_error',
1514 - esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1515 - 30
1516 - );
1517 - } else {
1518 - set_transient('mxchat_admin_notice_success',
1519 - esc_html__('Content successfully submitted!', 'mxchat'),
1520 - 30
1521 - );
1529 + // Display success message if all prompts were deleted
1530 + if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
1531 + echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
1522 1532 }
1523 1533
1524 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1525 - exit;
1526 -}
1534 + // Set up pagination and search query
1535 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
1536 + $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
1537 + $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
1538 + $per_page = 10;
1527 1539
1528 -private function is_pdf_url($url, $response) {
1529 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1530 - $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1540 + //error_log('DEBUG: Search query: ' . $search_query);
1541 + //error_log('DEBUG: Current page: ' . $current_page);
1542 + //error_log('DEBUG: Per page: ' . $per_page);
1531 1543
1532 - return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1533 -}
1534 -private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1535 - if (!current_user_can('manage_options')) {
1536 - //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat'));
1537 - return false;
1544 + // ================================
1545 + // MULTI-BOT CONFIGURATION
1546 + // ================================
1547 +
1548 + // Check for multi-bot and set up bot selection
1549 + if (class_exists('MxChat_Multi_Bot_Manager')) {
1550 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
1551 + $available_bots = $multi_bot_manager->get_available_bots();
1552 +
1553 + // Get saved bot selection (user-specific first, then site-wide default)
1554 + $user_id = get_current_user_id();
1555 + $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
1556 + if (empty($saved_bot_id)) {
1557 + $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
1558 + }
1559 +
1560 + // Allow URL override but default to saved selection
1561 + $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
1562 + $multibot_active = true;
1563 + } else {
1564 + $current_bot_id = 'default';
1565 + $multibot_active = false;
1538 1566 }
1539 1567
1540 - $pdf_url = esc_url_raw($pdf_url);
1541 - $upload_dir = wp_upload_dir();
1568 +// ================================
1569 +// REPLACE THIS SECTION WITH UNIFIED TABLE LOGIC
1570 +// ================================
1542 1571
1543 - if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1544 - //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error'])));
1545 - return false;
1546 - }
1572 +// Get bot-specific Pinecone settings
1573 +$pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($current_bot_id);
1574 +$use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
1575 +$pinecone_api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
1547 1576
1548 - $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1549 - $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1577 +error_log('DEBUG: Bot ' . $current_bot_id . ' - Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
1578 +error_log('DEBUG: Bot ' . $current_bot_id . ' - Has API Key: ' . (!empty($pinecone_api_key) ? 'YES' : 'NO'));
1579 +error_log('DEBUG: Bot ' . $current_bot_id . ' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
1580 +error_log('DEBUG: Bot ' . $current_bot_id . ' - Namespace: ' . ($pinecone_options['mxchat_pinecone_namespace'] ?? 'NOT SET'));
1550 1581
1551 - $response_body = wp_remote_retrieve_body($response);
1552 - if (empty($response_body)) {
1553 - //error_log(esc_html__('Empty PDF response body', 'mxchat'));
1554 - return false;
1555 - }
1582 +if ($use_pinecone && !empty($pinecone_api_key)) {
1583 + error_log('DEBUG: Using PINECONE data source with bot-specific config');
1584 + // PINECONE DATA SOURCE
1585 + $data_source = 'pinecone';
1586 +
1587 + // IMPORTANT: Pass the bot-specific $pinecone_options, not default options!
1588 + $records = $pinecone_manager->mxchat_fetch_pinecone_records($pinecone_options, $search_query, $current_page, $per_page, $current_bot_id);
1556 1589
1557 - if (!wp_mkdir_p(dirname($pdf_path))) {
1558 - //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path)));
1559 - return false;
1560 - }
1590 + // TEMPORARY DEBUG - Add this right after the fetch call
1591 + error_log('=== DEBUG: Bot switching issue ===');
1592 + error_log('Current bot ID: ' . $current_bot_id);
1593 + error_log('Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
1594 + error_log('Records returned: ' . count($records['data'] ?? []));
1595 + error_log('Total records: ' . ($records['total'] ?? 0));
1561 1596
1562 - try {
1563 - file_put_contents($pdf_path, $response_body);
1564 -
1565 - if (!file_exists($pdf_path)) {
1566 - throw new Exception(__('Failed to save PDF file', 'mxchat'));
1597 + // Check first few records to see their bot_id
1598 + if (!empty($records['data'])) {
1599 + foreach (array_slice($records['data'], 0, 3) as $i => $record) {
1600 + $record_bot_id = $record->bot_id ?? 'NOT_SET';
1601 + error_log('Record ' . ($i+1) . ' bot_id: ' . $record_bot_id . ', content preview: ' . substr($record->article_content ?? '', 0, 30) . '...');
1567 1602 }
1603 + }
1604 + error_log('=== END DEBUG ===');
1568 1605
1569 - $parser = new \Smalot\PdfParser\Parser();
1570 - $pdf = $parser->parseFile($pdf_path);
1571 - $total_pages = absint(count($pdf->getPages()));
1606 + $total_records = $records['total'] ?? 0;
1607 + $prompts = $records['data'] ?? array();
1608 + $total_in_database = $records['total_in_database'] ?? 0;
1609 + $showing_recent_only = $records['showing_recent_only'] ?? false;
1572 1610
1573 - if ($total_pages < 1) {
1574 - throw new Exception(__('Invalid PDF: no pages found', 'mxchat'));
1575 - }
1611 + $total_pages = ceil($total_records / $per_page);
1576 1612
1577 - wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1578 - 'pdf_path' => $pdf_path,
1579 - 'pdf_url' => $pdf_url,
1580 - 'total_pages' => $total_pages,
1581 - 'batch_size' => absint(15),
1582 - 'batch_pause' => absint(10)
1583 - ));
1613 +} else {
1614 + error_log('DEBUG: Using WORDPRESS DB data source');
1615 + // WORDPRESS DB DATA SOURCE (your existing logic)
1616 + $data_source = 'wordpress';
1584 1617
1585 - $status_data = array(
1586 - 'total_pages' => $total_pages,
1587 - 'processed_pages' => 0,
1588 - 'status' => 'processing',
1589 - 'last_update' => time()
1590 - );
1618 + // Initialize these variables for WordPress DB
1619 + $total_in_database = 0;
1620 + $showing_recent_only = false;
1591 1621
1592 - set_transient(
1593 - sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1594 - array_map('sanitize_text_field', $status_data),
1595 - DAY_IN_SECONDS
1596 - );
1622 + $offset = ($current_page - 1) * $per_page;
1597 1623
1598 - return __('scheduled', 'mxchat');
1599 -
1600 - } catch (Exception $e) {
1601 - //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1602 - if (file_exists($pdf_path)) {
1603 - wp_delete_file($pdf_path);
1604 - }
1605 - return false;
1624 + // Modify query to handle search input
1625 + $sql_search = "";
1626 + if ($search_query) {
1627 + $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
1606 1628 }
1607 -}
1608 -public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1609 - // Validate inputs
1610 - $pdf_path = sanitize_text_field($pdf_path);
1611 - $pdf_url = esc_url_raw($pdf_url);
1612 - $total_pages = absint($total_pages);
1613 - $batch_size = absint($batch_size);
1614 - $batch_pause = absint($batch_pause);
1615 1629
1616 - try {
1617 - if (!file_exists($pdf_path)) {
1618 - throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1619 - }
1630 + // Retrieve total number of prompts, considering search filter
1631 + $count_query = "SELECT COUNT(*) FROM {$table_name} {$sql_search}";
1632 + $total_records = $wpdb->get_var($count_query);
1633 + $total_pages = ceil($total_records / $per_page);
1620 1634
1621 - $parser = new \Smalot\PdfParser\Parser();
1622 - $pdf = $parser->parseFile($pdf_path);
1623 - $pages = $pdf->getPages();
1635 + // Retrieve prompts from the database
1636 + $prompts_query = $wpdb->prepare(
1637 + "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
1638 + $per_page,
1639 + $offset
1640 + );
1624 1641
1625 - // Get current progress
1626 - $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1627 - $status = get_transient($status_key);
1642 + $prompts = $wpdb->get_results($prompts_query);
1643 +}
1628 1644
1629 - if (!$status || !is_array($status)) {
1630 - throw new Exception('Invalid status data retrieved from transient');
1631 - }
1645 + // ================================
1646 + // END OF REPLACEMENT SECTION
1647 + // ================================
1632 1648
1633 - $start_page = absint($status['processed_pages']);
1634 - $end_page = min($start_page + $batch_size, $total_pages);
1649 + // Retrieve processing statuses
1650 + $pdf_url = get_transient('mxchat_last_pdf_url');
1651 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
1652 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1653 + $pdf_status = $pdf_url ? $knowledge_manager->mxchat_get_pdf_processing_status($pdf_url) : false;
1654 + $sitemap_status = $sitemap_url ? $knowledge_manager->mxchat_get_sitemap_processing_status($sitemap_url) : false;
1635 1655
1636 - $instance = new self(); // Create an instance of the class
1656 + $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
1657 + || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
1637 1658
1638 - for ($i = $start_page; $i < $end_page; $i++) {
1639 - $text = $pages[$i]->getText();
1640 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1659 + //error_log('=== DEBUG: mxchat_create_prompts_page data preparation completed ===');
1641 1660
1642 - if (!empty($sanitized_content)) {
1643 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1644 - if (is_array($embedding_vector)) {
1645 - $metadata = array(
1646 - 'document_type' => 'pdf',
1647 - 'total_pages' => $total_pages,
1648 - 'current_page' => $i + 1,
1649 - 'prev_page' => $i > 0 ? $i : null,
1650 - 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1651 - 'source_url' => $pdf_url
1652 - );
1661 + ?>
1653 1662
1654 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1655 - $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1663 + <div class="wrap mxchat-wrapper">
1664 + <!-- Hero Section -->
1665 + <div class="mxchat-hero">
1666 + <h1 class="mxchat-main-title">
1667 + <span class="mxchat-gradient-text">Knowledge Base</span> Manager
1668 + </h1>
1669 + <p class="mxchat-hero-subtitle">
1670 + <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
1671 + </p>
1672 + </div>
1656 1673
1657 - $options = get_option('mxchat_options');
1658 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1674 + <div class="mxchat-content">
1675 +
1676 + <!-- Multi-Bot Selector (only shows if multi-bot addon is active) -->
1677 + <?php if (class_exists('MxChat_Multi_Bot_Manager')) :
1678 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
1679 + $available_bots = $multi_bot_manager->get_available_bots();
1680 +
1681 + // Get saved bot selection (user-specific first, then site-wide default)
1682 + $user_id = get_current_user_id();
1683 + $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
1684 + if (empty($saved_bot_id)) {
1685 + $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
1659 1686 }
1687 +
1688 + // Allow URL override but default to saved selection
1689 + $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
1690 + ?>
1691 + <div class="mxchat-card" style="margin-bottom: 20px;">
1692 + <div class="mxchat-bot-selector-wrapper" style="display: flex; align-items: center; gap: 15px;">
1693 + <label for="mxchat-bot-selector" style="font-weight: 600;">
1694 + <?php esc_html_e('Select Bot Database:', 'mxchat'); ?>
1695 + </label>
1696 + <select id="mxchat-bot-selector" class="mxchat-bot-selector" style="min-width: 200px;">
1697 + <?php foreach ($available_bots as $bot_id => $bot_name) : ?>
1698 + <option value="<?php echo esc_attr($bot_id); ?>" <?php selected($current_bot_id, $bot_id); ?>>
1699 + <?php echo esc_html($bot_name); ?>
1700 + </option>
1701 + <?php endforeach; ?>
1702 + </select>
1703 + <span class="mxchat-bot-info" style="color: #666; font-size: 13px;">
1704 + <?php esc_html_e('Content will be added to the selected bot\'s knowledge base', 'mxchat'); ?>
1705 + </span>
1706 + <span id="mxchat-bot-save-status" style="display: none; color: #00a32a; font-size: 13px;">
1707 + ✓ <?php esc_html_e('Saved', 'mxchat'); ?>
1708 + </span>
1709 + </div>
1710 + </div>
1711 +
1712 + <script>
1713 +jQuery(document).ready(function($) {
1714 + var saveTimer;
1715 +
1716 + // Function to update bot_id in forms
1717 + function updateBotIdInForm(formSelector) {
1718 + var botId = $('#mxchat-bot-selector').val();
1719 + var form = $(formSelector);
1720 +
1721 + if (form.length > 0) {
1722 + // Remove existing bot_id hidden input
1723 + form.find('input[name="bot_id"]').remove();
1724 +
1725 + // Add new bot_id hidden input if not default
1726 + if (botId && botId !== 'default') {
1727 + form.append('<input type="hidden" name="bot_id" value="' + botId + '">');
1660 1728 }
1661 -
1662 - // Update progress with sanitized data
1663 - $status['processed_pages'] = absint($i + 1);
1664 - $status['last_update'] = time();
1665 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1666 1729 }
1667 -
1668 - // Schedule next batch if needed
1669 - if ($end_page < $total_pages) {
1670 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
1671 - 'pdf_path' => $pdf_path,
1672 - 'pdf_url' => $pdf_url,
1673 - 'total_pages' => $total_pages,
1674 - 'batch_size' => $batch_size,
1675 - 'batch_pause' => $batch_pause
1676 - ));
1677 - } else {
1678 - // Processing complete
1679 - $status['status'] = 'complete';
1680 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1681 - if (file_exists($pdf_path)) {
1682 - wp_delete_file($pdf_path);
1730 + }
1731 +
1732 + $('#mxchat-bot-selector').on('change', function() {
1733 + var botId = $(this).val();
1734 +
1735 + // Clear any existing timer
1736 + clearTimeout(saveTimer);
1737 +
1738 + // Update all forms with new bot_id when bot selection changes
1739 + updateBotIdInForm('#mxchat-url-form');
1740 + updateBotIdInForm('#mxchat-content-form');
1741 +
1742 + // Save the selection via AJAX
1743 + $.ajax({
1744 + url: ajaxurl,
1745 + type: 'POST',
1746 + data: {
1747 + action: 'mxchat_save_selected_bot',
1748 + bot_id: botId,
1749 + nonce: '<?php echo wp_create_nonce('mxchat_save_setting_nonce'); ?>'
1750 + },
1751 + success: function(response) {
1752 + if (response.success) {
1753 + // Show saved indicator
1754 + $('#mxchat-bot-save-status').fadeIn().delay(2000).fadeOut();
1755 +
1756 + // Reload the page after a short delay to refresh content
1757 + saveTimer = setTimeout(function() {
1758 + var currentUrl = new URL(window.location.href);
1759 + currentUrl.searchParams.set('bot_id', botId);
1760 + currentUrl.searchParams.set('page', 'mxchat-prompts');
1761 + window.location.href = currentUrl.toString();
1762 + }, 500);
1763 + }
1764 + },
1765 + error: function() {
1766 + console.error('Failed to save bot selection');
1683 1767 }
1684 - }
1768 + });
1769 + });
1770 +
1771 + // Initialize forms with current bot_id when the page loads
1772 + setTimeout(function() {
1773 + updateBotIdInForm('#mxchat-url-form');
1774 + updateBotIdInForm('#mxchat-content-form');
1775 + }, 100);
1776 +});
1777 +</script>
1685 1778
1686 - } catch (\Exception $e) {
1687 - $status['status'] = 'error';
1688 - $status['error'] = sanitize_text_field($e->getMessage());
1689 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1690 - if (file_exists($pdf_path)) {
1691 - wp_delete_file($pdf_path);
1692 - }
1693 - }
1694 -}
1695 -public function get_pdf_processing_status($pdf_url) {
1696 - $pdf_url = esc_url_raw($pdf_url);
1697 - $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1779 + <?php
1780 + // Set a flag so we know multi-bot is active throughout the page
1781 + $multibot_active = true;
1782 + else:
1783 + $current_bot_id = 'default';
1784 + $multibot_active = false;
1785 + endif;
1786 + ?>
1698 1787
1699 - if (!$status || !is_array($status)) {
1700 - return false;
1701 - }
1788 + <!-- Tab Navigation -->
1789 + <div class="mxchat-kb-tabs-nav">
1790 + <button class="mxchat-kb-tab-button active" data-tab="import">
1791 + <?php esc_html_e('Knowledge Import', 'mxchat'); ?>
1792 + </button>
1793 + <button class="mxchat-kb-tab-button" data-tab="sync">
1794 + <?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?>
1795 + </button>
1796 + <button class="mxchat-kb-tab-button" data-tab="pinecone">
1797 + <?php esc_html_e('Pinecone Settings', 'mxchat'); ?>
1798 + </button>
1799 + </div>
1702 1800
1703 - return array(
1704 - 'total_pages' => absint($status['total_pages']),
1705 - 'processed_pages' => absint($status['processed_pages']),
1706 - 'percentage' => ($status['total_pages'] > 0)
1707 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1708 - : 0,
1709 - 'status' => sanitize_text_field($status['status']),
1710 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1711 - );
1712 -}
1713 -public function mxchat_handle_sitemap_submission() {
1714 - // Check if the form was submitted and verify permissions
1715 - if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
1716 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
1717 - }
1801 + <div class="mxchat-kb-tabs-content">
1802 + <div id="mxchat-kb-tab-import" class="mxchat-kb-tab-content active">
1803 + <!-- Import Options Card -->
1804 + <div class="mxchat-card">
1718 1805
1719 - // Verify nonce
1720 - check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1806 + <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
1721 1807
1722 - // Validate URL
1723 - if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
1724 - set_transient('mxchat_admin_notice_error',
1725 - esc_html__('Please provide a valid URL.', 'mxchat'),
1726 - 30
1727 - );
1728 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1729 - exit;
1730 - }
1808 + <?php
1809 + // Check if the appropriate embedding API key exists
1810 + $embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
1811 + $has_openai_key = !empty($this->options['api_key']);
1812 + $has_voyage_key = !empty($this->options['voyage_api_key']);
1813 + $has_gemini_key = !empty($this->options['gemini_api_key']);
1731 1814
1732 - $submitted_url = esc_url_raw($_POST['sitemap_url']);
1733 - $response = wp_remote_get($submitted_url, array('timeout' => 30));
1815 + // Determine if they have the needed API key for their selected embedding model
1816 + $has_required_key = false;
1817 + $required_key_type = '';
1734 1818
1735 - if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1736 - $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat');
1737 - set_transient('mxchat_admin_notice_error',
1738 - sprintf(
1739 - esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1740 - esc_html($error_message)
1741 - ),
1742 - 30
1743 - );
1744 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1745 - exit;
1746 - }
1819 + if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
1820 + $has_required_key = true;
1821 + $required_key_type = 'OpenAI';
1822 + } elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
1823 + $has_required_key = true;
1824 + $required_key_type = 'Voyage AI';
1825 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false && $has_gemini_key) {
1826 + $has_required_key = true;
1827 + $required_key_type = 'Google Gemini';
1828 + } elseif (strpos($embedding_model, 'text-embedding-') !== false) {
1829 + $required_key_type = 'OpenAI';
1830 + } elseif (strpos($embedding_model, 'voyage-') !== false) {
1831 + $required_key_type = 'Voyage AI';
1832 + } elseif (strpos($embedding_model, 'gemini-embedding-') !== false) {
1833 + $required_key_type = 'Google Gemini';
1834 + }
1835 + ?>
1747 1836
1748 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1749 - $body_content = wp_remote_retrieve_body($response);
1837 + <div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
1838 + <?php if ($has_required_key): ?>
1839 + <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>
1840 + <?php else: ?>
1841 + <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>
1842 + <?php endif; ?>
1843 + </div>
1750 1844
1751 - if (empty($body_content)) {
1752 - set_transient('mxchat_admin_notice_error',
1753 - esc_html__('Empty response received from URL.', 'mxchat'),
1754 - 30
1755 - );
1756 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1757 - exit;
1758 - }
1845 + <!-- Import Options Section -->
1846 + <div class="mxchat-import-section">
1847 + <h3><?php esc_html_e('Import Options', 'mxchat'); ?></h3>
1759 1848
1760 - // Handle PDF URL
1761 - if ($this->is_pdf_url($submitted_url, $response)) {
1762 - $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
1849 + <!-- Import Options Grid -->
1850 + <div class="mxchat-import-options">
1851 + <!-- WordPress Import Option -->
1852 + <button type="button" id="mxchat-open-content-selector" class="mxchat-import-box mxchat-import-wordpress" data-option="wordpress">
1853 + <div class="mxchat-import-icon">
1854 + <span class="dashicons dashicons-wordpress"></span>
1855 + </div>
1856 + <div class="mxchat-import-content">
1857 + <h4><?php esc_html_e('WordPress Content', 'mxchat'); ?></h4>
1858 + <p><?php esc_html_e('Import specific posts and pages to your knowledge base.', 'mxchat'); ?></p>
1859 + </div>
1860 + <div class="mxchat-recommended-tag"><?php esc_html_e('Recommended', 'mxchat'); ?></div>
1861 + </button>
1763 1862
1764 - if ($result === 'scheduled') {
1765 - set_transient(
1766 - 'mxchat_last_pdf_url',
1767 - sanitize_text_field($submitted_url),
1768 - DAY_IN_SECONDS
1769 - );
1770 - set_transient('mxchat_admin_notice_info',
1771 - esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1772 - 30
1773 - );
1774 - } else {
1775 - set_transient('mxchat_admin_notice_error',
1776 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
1777 - 30
1778 - );
1779 - }
1863 + <!-- Sitemap Import Option -->
1864 + <button type="button" class="mxchat-import-box" data-option="sitemap" data-placeholder="<?php esc_attr_e('Enter sitemap URL here', 'mxchat'); ?>" data-type="sitemap">
1865 + <div class="mxchat-import-icon">
1866 + <span class="dashicons dashicons-admin-site-alt"></span>
1867 + </div>
1868 + <div class="mxchat-import-content">
1869 + <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1870 + <p><?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
1871 + </div>
1872 + </button>
1780 1873
1781 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1782 - exit;
1783 - }
1874 + <!-- Direct URL Import Option -->
1875 + <button type="button" class="mxchat-import-box" data-option="url" data-placeholder="<?php esc_attr_e('Enter webpage URL here', 'mxchat'); ?>" data-type="url">
1876 + <div class="mxchat-import-icon">
1877 + <span class="dashicons dashicons-admin-links"></span>
1878 + </div>
1879 + <div class="mxchat-import-content">
1880 + <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
1881 + <p><?php esc_html_e('Import content from any webpage.', 'mxchat'); ?></p>
1882 + </div>
1883 + </button>
1784 1884
1785 - // Handle Sitemap XML
1786 - if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
1787 - libxml_use_internal_errors(true);
1788 - $xml = simplexml_load_string($body_content);
1789 - $xml_errors = libxml_get_errors();
1790 - libxml_clear_errors();
1885 + <!-- Direct Content Import Option -->
1886 + <button type="button" class="mxchat-import-box" data-option="content" data-type="content">
1887 + <div class="mxchat-import-icon">
1888 + <span class="dashicons dashicons-editor-paste-text"></span>
1889 + </div>
1890 + <div class="mxchat-import-content">
1891 + <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
1892 + <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p>
1893 + </div>
1894 + </button>
1791 1895
1792 - if ($xml === false || !empty($xml_errors)) {
1793 - set_transient('mxchat_admin_notice_error',
1794 - esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1795 - 30
1796 - );
1797 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1798 - exit;
1799 - }
1896 + <!-- PDF Import Option -->
1897 + <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf">
1898 + <div class="mxchat-import-icon">
1899 + <span class="dashicons dashicons-media-document"></span>
1900 + </div>
1901 + <div class="mxchat-import-content">
1902 + <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1903 + <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p>
1904 + </div>
1905 + </button>
1906 + </div>
1800 1907
1801 - $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
1908 + <!-- Input Areas for URL and Content -->
1909 + <div class="mxchat-import-input-area" id="mxchat-url-input-area" style="display: none;">
1910 + <?php if (!$is_processing) : ?>
1911 + <form id="mxchat-url-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
1912 + <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
1913 + <input type="hidden" name="import_type" id="import_type" value="url">
1914 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
1915 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
1916 + <?php endif; ?>
1917 + <div class="mxchat-url-input-group">
1918 + <input type="url"
1919 + name="sitemap_url"
1920 + id="sitemap_url"
1921 + placeholder="<?php esc_attr_e('Enter URL here', 'mxchat'); ?>"
1922 + required />
1923 + <button type="submit"
1924 + name="submit_sitemap"
1925 + class="mxchat-button-primary">
1926 + <?php esc_html_e('Import', 'mxchat'); ?>
1927 + </button>
1928 + </div>
1929 + <p class="mxchat-url-description" id="url-description-text"></p>
1930 + </form>
1931 + <?php endif; ?>
1932 + </div>
1802 1933
1803 - if ($result === 'scheduled') {
1804 - set_transient(
1805 - 'mxchat_last_sitemap_url',
1806 - sanitize_text_field($submitted_url),
1807 - DAY_IN_SECONDS
1808 - );
1809 - set_transient('mxchat_admin_notice_info',
1810 - esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1811 - 30
1812 - );
1813 - } else {
1814 - set_transient('mxchat_admin_notice_error',
1815 - esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
1816 - 30
1817 - );
1818 - }
1934 + <div class="mxchat-import-input-area" id="mxchat-content-input-area" style="display: none;">
1935 + <?php if (!$is_processing) : ?>
1936 + <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
1937 + <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
1938 + <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
1939 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
1940 + <?php endif; ?>
1941 + <div class="mxchat-form-group">
1942 + <textarea
1943 + name="article_content"
1944 + id="article_content"
1945 + placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"
1946 + required
1947 + rows="6"
1948 + ></textarea>
1949 + </div>
1950 + <div class="mxchat-form-group">
1951 + <input type="url"
1952 + name="article_url"
1953 + id="article_url"
1954 + placeholder="<?php esc_attr_e('Enter source URL (Optional)', 'mxchat'); ?>">
1955 + </div>
1956 + <button type="submit"
1957 + name="submit_content"
1958 + class="mxchat-button-primary">
1959 + <?php esc_html_e('Import Content', 'mxchat'); ?>
1960 + </button>
1961 + </form>
1962 + <?php endif; ?>
1963 + </div>
1964 + </div>
1819 1965
1820 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1821 - exit;
1822 - }
1966 + <!-- PDF Processing Status - ONLY PROCESSING -->
1967 + <?php if ($pdf_status && $pdf_status['status'] === 'processing') : ?>
1968 + <div class="mxchat-status-card" data-card-type="pdf">
1969 + <div class="mxchat-status-header">
1970 + <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1823 1971
1824 - // Handle Regular URL
1825 - $page_content = $this->mxchat_extract_main_content($body_content);
1826 - $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
1972 + <!-- Processing controls -->
1973 + <div class="mxchat-processing-controls">
1974 + <form method="post" class="mxchat-stop-form"
1975 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1976 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1977 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1978 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1979 + </button>
1980 + </form>
1827 1981
1828 - if (empty($sanitized_content)) {
1829 - set_transient('mxchat_admin_notice_error',
1830 - esc_html__('No valid content found on the provided URL.', 'mxchat'),
1831 - 30
1832 - );
1833 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1834 - exit;
1835 - }
1982 + <button type="button" class="mxchat-manual-batch-btn"
1983 + data-process-type="pdf"
1984 + data-url="<?php echo esc_attr(get_transient('mxchat_last_pdf_url')); ?>">
1985 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
1986 + </button>
1987 + </div>
1988 + </div>
1836 1989
1837 - $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
1838 - if (is_array($embedding_vector)) {
1839 - MxChat_Utils::submit_content_to_db(
1840 - $sanitized_content,
1841 - $submitted_url,
1842 - $this->options['api_key']
1843 - );
1844 - set_transient('mxchat_admin_notice_success',
1845 - esc_html__('URL content successfully submitted!', 'mxchat'),
1846 - 30
1847 - );
1848 - } else {
1849 - set_transient('mxchat_admin_notice_error',
1850 - esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
1851 - 30
1852 - );
1853 - }
1990 + <div class="mxchat-progress-bar">
1991 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1992 + </div>
1854 1993
1855 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1856 - exit;
1857 -}
1858 -private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
1859 - if (!current_user_can('manage_options')) {
1860 - //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
1861 - return false;
1862 - }
1994 + <div class="mxchat-status-details">
1995 + <p><?php printf(
1996 + esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1997 + absint($pdf_status['processed_pages']),
1998 + absint($pdf_status['total_pages']),
1999 + absint($pdf_status['percentage'])
2000 + ); ?></p>
1863 2001
1864 - try {
1865 - $sitemap_url = esc_url_raw($sitemap_url);
2002 + <?php if (!empty($pdf_status['failed_pages']) && $pdf_status['failed_pages'] > 0) : ?>
2003 + <p><strong><?php esc_html_e('Failed pages:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['failed_pages']); ?></p>
2004 + <?php endif; ?>
1866 2005
1867 - if (!$xml || !is_object($xml)) {
1868 - throw new Exception(__('Invalid XML object provided', 'mxchat'));
1869 - }
2006 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($pdf_status['status'])); ?></p>
2007 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['last_update']); ?></p>
2008 + </div>
2009 + </div>
2010 + <?php endif; ?>
1870 2011
1871 - $urls = [];
1872 - foreach ($xml->url as $url_element) {
1873 - $url = esc_url_raw((string)$url_element->loc);
1874 - if ($url) {
1875 - $urls[] = $url;
1876 - }
1877 - }
2012 + <!-- Sitemap Processing Status - ONLY PROCESSING -->
2013 + <?php if ($sitemap_status && $sitemap_status['status'] === 'processing') : ?>
2014 + <div class="mxchat-status-card" data-card-type="sitemap">
2015 + <div class="mxchat-status-header">
2016 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
1878 2017
1879 - $total_urls = absint(count($urls));
2018 + <!-- Processing controls -->
2019 + <div class="mxchat-processing-controls">
2020 + <form method="post" class="mxchat-stop-form"
2021 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2022 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2023 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2024 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2025 + </button>
2026 + </form>
1880 2027
1881 - if ($total_urls < 1) {
1882 - throw new Exception(__('No valid URLs found in sitemap', 'mxchat'));
1883 - }
2028 + <button type="button" class="mxchat-manual-batch-btn"
2029 + data-process-type="sitemap"
2030 + data-url="<?php echo esc_attr(get_transient('mxchat_last_sitemap_url')); ?>">
2031 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
2032 + </button>
2033 + </div>
2034 + </div>
1884 2035
1885 - wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
1886 - 'urls' => $urls,
1887 - 'sitemap_url' => $sitemap_url,
1888 - 'total_urls' => $total_urls,
1889 - 'batch_size' => absint(10),
1890 - 'batch_pause' => absint(5)
1891 - ));
2036 + <div class="mxchat-progress-bar">
2037 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
2038 + </div>
1892 2039
1893 - $status_data = array(
1894 - 'total_urls' => $total_urls,
1895 - 'processed_urls' => 0,
1896 - 'status' => 'processing',
1897 - 'last_update' => time()
1898 - );
2040 + <div class="mxchat-status-details">
2041 + <p><?php printf(
2042 + esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
2043 + absint($sitemap_status['processed_urls']),
2044 + absint($sitemap_status['total_urls']),
2045 + absint($sitemap_status['percentage'])
2046 + ); ?></p>
1899 2047
1900 - set_transient(
1901 - sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
1902 - array_map('sanitize_text_field', $status_data),
1903 - DAY_IN_SECONDS
1904 - );
2048 + <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
2049 + <p><strong><?php esc_html_e('Failed URLs:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['failed_urls']); ?></p>
2050 + <?php endif; ?>
1905 2051
1906 - return __('scheduled', 'mxchat');
2052 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($sitemap_status['status'])); ?></p>
2053 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['last_update']); ?></p>
1907 2054
1908 - } catch (\Exception $e) {
1909 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1910 - return false;
1911 - }
1912 -}
1913 -public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1914 - // Validate inputs
1915 - $sitemap_url = esc_url_raw($sitemap_url);
1916 - $total_urls = absint($total_urls);
1917 - $batch_size = absint($batch_size);
1918 - $batch_pause = absint($batch_pause);
2055 + <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
2056 + <div class="mxchat-error-notice">
2057 + <?php if (!empty($sitemap_status['error'])) : ?>
2058 + <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
2059 + <?php endif; ?>
2060 + <?php if (!empty($sitemap_status['last_error'])) : ?>
2061 + <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
2062 + <?php endif; ?>
2063 + </div>
2064 + <?php endif; ?>
2065 + </div>
2066 + </div>
2067 + <?php endif; ?>
1919 2068
1920 - if (!is_array($urls) || empty($urls)) {
1921 - return;
1922 - }
2069 + <!-- Single URL Submission Status -->
2070 + <?php
2071 + // Get single URL status
2072 + $single_url_status = $this->knowledge_manager->mxchat_get_single_url_status();
2073 + $is_active_processing =
2074 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
2075 + ($pdf_status && $pdf_status['status'] === 'processing');
2076 + ?>
1923 2077
1924 - try {
1925 - $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
1926 - $status = get_transient($status_key);
2078 + <div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
2079 + <?php if ($single_url_status && !$is_active_processing) : ?>
2080 + <div class="mxchat-status-card">
2081 + <div class="mxchat-status-header">
2082 + <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
2083 + <?php if ($single_url_status['status'] === 'failed') : ?>
2084 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
2085 + <?php else : ?>
2086 + <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
2087 + <?php endif; ?>
2088 + </div>
2089 + <div class="mxchat-status-details">
2090 + <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
2091 + <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
2092 + <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
2093 + </a>
2094 + </p>
2095 + <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
1927 2096
1928 - if (!$status || !is_array($status)) {
1929 - throw new Exception('Invalid status data retrieved from transient');
1930 - }
2097 + <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
2098 + <div class="mxchat-error-notice">
2099 + <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
2100 + </div>
2101 + <?php endif; ?>
1931 2102
1932 - $start_url = absint($status['processed_urls']);
1933 - $end_url = min($start_url + $batch_size, $total_urls);
1934 - $instance = new self();
2103 + <?php if ($single_url_status['status'] === 'complete') : ?>
2104 + <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>
2105 + <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
2106 + <?php endif; ?>
2107 + </div>
2108 + </div>
2109 + <?php endif; ?>
2110 + </div>
1935 2111
1936 - // Track failures in batch
1937 - $batch_stats = [
1938 - 'processed' => 0,
1939 - 'failed' => 0,
1940 - 'last_error' => ''
1941 - ];
2112 + <!-- Completed Status Cards - Handles completed PDF/Sitemap processing -->
2113 + <?php
2114 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
2115 + echo $knowledge_manager->mxchat_render_completed_status_cards();
2116 + ?>
1942 2117
1943 - for ($i = $start_url; $i < $end_url; $i++) {
1944 - $page_url = esc_url_raw($urls[$i]);
1945 - $page_response = wp_remote_get($page_url);
2118 + </div>
1946 2119
1947 - if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1948 - $batch_stats['failed']++;
1949 - continue;
1950 - }
2120 + <!-- Knowledge Base Table Card -->
2121 + <div class="mxchat-card">
2122 + <div class="mxchat-card-header">
2123 + <h2>
2124 + <?php esc_html_e('Knowledge Base', 'mxchat'); ?>
2125 + <span class="mxchat-record-count">
2126 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2127 + (<?php echo esc_html($total_records); ?> of <?php echo esc_html(number_format($total_in_database)); ?> total - recent entries only)
2128 + <?php else: ?>
2129 + (<?php echo esc_html($total_records); ?>)
2130 + <?php endif; ?>
2131 + </span>
2132 + <!-- Keep your existing badge code here -->
2133 + <?php if ($use_pinecone) : ?>
2134 + <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;">
2135 + <span class="dashicons dashicons-cloud"></span>
2136 + <?php esc_html_e('Pinecone', 'mxchat'); ?>
2137 + </span>
2138 + <?php else : ?>
2139 + <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;">
2140 + <span class="dashicons dashicons-database-view"></span>
2141 + <?php esc_html_e('WordPress DB', 'mxchat'); ?>
2142 + </span>
2143 + <?php endif; ?>
2144 + </h2>
2145 + <div class="mxchat-header-actions">
2146 + <!-- Search -->
2147 + <form method="get" id="knowledge-search" class="mxchat-search-form">
2148 + <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
2149 + <input type="hidden" name="page" value="mxchat-prompts" />
2150 + <?php if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') : ?>
2151 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2152 + <?php endif; ?>
2153 + <div class="mxchat-search-group">
2154 + <span class="dashicons dashicons-search"></span>
2155 + <input type="text"
2156 + name="search"
2157 + placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
2158 + value="<?php echo esc_attr($search_query); ?>" />
2159 + </div>
2160 + </form>
1951 2161
1952 - $page_html = wp_remote_retrieve_body($page_response);
1953 - $page_content = $instance->mxchat_extract_main_content($page_html);
1954 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2162 +<form method="post"
2163 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
2164 + class="mxchat-delete-form"
2165 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
2166 + <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
2167 + <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" />
2168 + <!-- Always include bot_id, even if it's 'default' -->
2169 + <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2170 + <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
2171 + <span class="dashicons dashicons-trash"></span>
2172 + <?php esc_html_e('Delete All', 'mxchat'); ?>
2173 + </button>
2174 +</form>
2175 + </div>
2176 + </div>
1955 2177
1956 - if (!empty($sanitized_content)) {
1957 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2178 + <!-- Recent Entries Info Banner -->
2179 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2180 + <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;">
2181 + <span class="dashicons dashicons-info"></span>
2182 + <span>
2183 + <?php printf(
2184 + esc_html__('Showing your %s most recent entries for faster loading. Your complete database contains %s entries. ', 'mxchat'),
2185 + number_format($total_records),
2186 + number_format($total_in_database)
2187 + ); ?>
2188 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('View complete database in Pinecone →', 'mxchat'); ?></a>
2189 + </span>
2190 + </div>
2191 + <?php endif; ?>
1958 2192
1959 - if (is_array($embedding_vector)) {
1960 - $options = get_option('mxchat_options');
1961 - $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2193 + <!-- Data Source Info Banner -->
2194 + <?php if ($use_pinecone) : ?>
2195 + <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;">
2196 + <span class="dashicons dashicons-cloud"></span>
2197 + <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>
2198 + </div>
2199 + <?php else : ?>
2200 + <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;">
2201 + <span class="dashicons dashicons-database-view"></span>
2202 + <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>
2203 + </div>
2204 + <?php endif; ?>
1962 2205
1963 - if (is_wp_error($submission_result)) {
1964 - $batch_stats['failed']++;
1965 - $batch_stats['last_error'] = $submission_result->get_error_message();
1966 - continue;
1967 - }
2206 + <!-- Table -->
2207 + <div class="mxchat-table-wrapper">
2208 + <table class="mxchat-records-table">
2209 + <thead>
2210 + <tr>
2211 + <th><?php esc_html_e('ID', 'mxchat'); ?></th>
2212 + <th><?php esc_html_e('Content', 'mxchat'); ?></th>
2213 + <th><?php esc_html_e('Source', 'mxchat'); ?></th>
2214 + <?php if ($data_source === 'pinecone') : ?>
2215 + <th><?php esc_html_e('Vector ID', 'mxchat'); ?></th>
2216 + <?php endif; ?>
2217 + <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2218 + </tr>
2219 + </thead>
2220 + <tbody>
2221 + <?php if ($prompts) : ?>
2222 + <?php foreach ($prompts as $index => $prompt) : ?>
2223 + <tr id="prompt-<?php echo esc_attr($prompt->id); ?>"
2224 + data-source="<?php echo esc_attr($data_source); ?>"
2225 + <?php if ($data_source === 'pinecone') : ?>
2226 + style="background: rgba(33, 150, 243, 0.02);"
2227 + <?php endif; ?>>
2228 + <td>
2229 + <?php if ($data_source === 'pinecone') : ?>
2230 + <?php echo esc_html($index + 1 + (($current_page - 1) * $per_page)); ?>
2231 + <?php else : ?>
2232 + <?php echo esc_html($prompt->id); ?>
2233 + <?php endif; ?>
2234 + </td>
2235 + <td class="mxchat-content-cell">
2236 + <div class="content-view">
2237 + <?php
2238 + $content = $prompt->article_content;
2239 + // Check if content contains Hebrew characters
2240 + if (preg_match('/[\x{0590}-\x{05FF}]/u', $content)) {
2241 + // Apply RTL direction for Hebrew content
2242 + echo '<div dir="rtl" lang="he" class="rtl-content">';
2243 + echo wp_kses_post(wpautop(esc_textarea($content)));
2244 + echo '</div>';
2245 + } else {
2246 + echo wp_kses_post(wpautop(esc_textarea($content)));
2247 + }
2248 + ?>
2249 + </div>
2250 + <?php if ($data_source === 'wordpress') : ?>
2251 + <textarea class="content-edit" style="display:none;"
2252 + <?php if (preg_match('/[\x{0590}-\x{05FF}]/u', $prompt->article_content)) echo 'dir="rtl" lang="he"'; ?>>
2253 + <?php echo htmlspecialchars($prompt->article_content, ENT_QUOTES, 'UTF-8'); ?>
2254 + </textarea>
2255 + <?php endif; ?>
2256 + </td>
2257 + <td class="mxchat-url-cell">
2258 + <div class="url-view">
2259 + <?php if (!empty($prompt->source_url)) : ?>
2260 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
2261 + <span class="dashicons dashicons-external"></span>
2262 + <?php esc_html_e('View Source', 'mxchat'); ?>
2263 + </a>
2264 + <?php else : ?>
2265 + <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
2266 + <?php endif; ?>
2267 + </div>
2268 + <?php if ($data_source === 'wordpress') : ?>
2269 + <input type="text" class="url-edit" style="display:none;"
2270 + value="<?php echo htmlspecialchars($prompt->source_url, ENT_QUOTES, 'UTF-8'); ?>" />
2271 + <?php endif; ?>
2272 + </td>
2273 + <?php if ($data_source === 'pinecone') : ?>
2274 + <td class="mxchat-vector-id-cell">
2275 + <code style="background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 11px;">
2276 + <?php echo esc_html(substr($prompt->id, 0, 12) . '...'); ?>
2277 + </code>
2278 + </td>
2279 + <?php endif; ?>
2280 +
2281 + <td class="mxchat-actions-cell">
2282 + <?php if ($data_source === 'wordpress') : ?>
2283 + <!-- WordPress - Full editing capabilities -->
2284 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2285 + <select class="mxchat-role-select"
2286 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2287 + data-data-source="wordpress"
2288 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2289 + <?php
2290 + $current_role = $prompt->role_restriction ?? 'public';
2291 + $role_options = $knowledge_manager->mxchat_get_role_options();
2292 + foreach ($role_options as $role_key => $role_label) : ?>
2293 + <option value="<?php echo esc_attr($role_key); ?>"
2294 + <?php selected($current_role, $role_key); ?>>
2295 + <?php echo esc_html($role_label); ?>
2296 + </option>
2297 + <?php endforeach; ?>
2298 + </select>
2299 + </div>
2300 +
2301 + <!-- Edit/Save Buttons -->
2302 + <div class="mxchat-action-buttons">
2303 + <button class="mxchat-button-icon edit-button"
2304 + data-id="<?php echo esc_attr($prompt->id); ?>">
2305 + <span class="dashicons dashicons-edit"></span>
2306 + </button>
2307 + <button class="mxchat-button-icon save-button"
2308 + data-id="<?php echo esc_attr($prompt->id); ?>"
2309 + style="display:none;"
2310 + data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
2311 + <span class="dashicons dashicons-saved"></span>
2312 + </button>
2313 + </div>
2314 + <?php else : ?>
2315 + <!-- Pinecone - Role restriction editable, content read-only -->
2316 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2317 + <select class="mxchat-role-select"
2318 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2319 + data-data-source="pinecone"
2320 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2321 + <?php
2322 + $current_role = $prompt->role_restriction ?? 'public';
2323 + $role_options = $knowledge_manager->mxchat_get_role_options();
2324 + foreach ($role_options as $role_key => $role_label) : ?>
2325 + <option value="<?php echo esc_attr($role_key); ?>"
2326 + <?php selected($current_role, $role_key); ?>>
2327 + <?php echo esc_html($role_label); ?>
2328 + </option>
2329 + <?php endforeach; ?>
2330 + </select>
2331 + </div>
2332 +
2333 + <?php endif; ?>
1968 2334
1969 - $batch_stats['processed']++;
1970 - } else {
1971 - $batch_stats['failed']++;
1972 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
1973 - }
1974 - }
2335 + <!-- Delete button for both sources -->
2336 + <div class="mxchat-delete-container">
2337 + <?php if ($data_source === 'pinecone') : ?>
2338 + <!-- AJAX Delete for Pinecone -->
2339 + <button type="button"
2340 + class="mxchat-button-icon delete-button-ajax"
2341 + data-vector-id="<?php echo esc_attr($prompt->id); ?>"
2342 + data-bot-id="<?php echo esc_attr($current_bot_id); ?>"
2343 + data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
2344 + title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
2345 + <span class="dashicons dashicons-trash"></span>
2346 + </button>
2347 + <?php else : ?>
2348 + <!-- Regular link delete for WordPress DB -->
2349 + <a href="<?php echo esc_url(admin_url(
2350 + 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
2351 + . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
2352 + )); ?>"
2353 + class="mxchat-button-icon delete-button"
2354 + onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
2355 + <span class="dashicons dashicons-trash"></span>
2356 + </a>
2357 + <?php endif; ?>
2358 + </div>
2359 + </td>
1975 2360
1976 - $status['processed_urls'] = absint($i + 1);
1977 - $status['last_update'] = time();
1978 - $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
1979 - $status['last_error'] = $batch_stats['last_error'];
2361 + </tr>
2362 + <?php endforeach; ?>
2363 + <?php else : ?>
2364 + <tr>
2365 + <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
2366 + <?php if ($use_pinecone) : ?>
2367 + <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
2368 + <?php else : ?>
2369 + <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
2370 + <?php endif; ?>
2371 + </td>
2372 + </tr>
2373 + <?php endif; ?>
2374 + </tbody>
2375 + </table>
2376 + </div>
1980 2377
1981 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1982 - }
2378 + <?php if ($page_links) : ?>
2379 + <div class="mxchat-pagination">
2380 + <?php echo wp_kses_post($page_links); ?>
2381 + </div>
2382 + <?php endif; ?>
2383 + </div>
1983 2384
1984 - // If all URLs in this batch failed, stop processing
1985 - if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
1986 - $status['status'] = 'error';
1987 - $status['error'] = sprintf(
1988 - 'Processing stopped: %d consecutive failures. Last error: %s',
1989 - $batch_stats['failed'],
1990 - $batch_stats['last_error']
1991 - );
1992 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1993 - return;
1994 - }
2385 + </div>
1995 2386
1996 - if ($end_url < $total_urls) {
1997 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
1998 - 'urls' => array_map('esc_url_raw', $urls),
1999 - 'sitemap_url' => $sitemap_url,
2000 - 'total_urls' => $total_urls,
2001 - 'batch_size' => $batch_size,
2002 - 'batch_pause' => $batch_pause,
2003 - ));
2004 - } else {
2005 - $status['status'] = 'complete';
2006 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2007 - }
2008 - } catch (\Exception $e) {
2009 - $status['status'] = 'error';
2010 - $status['error'] = sanitize_text_field($e->getMessage());
2011 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2012 - }
2013 -}
2387 + <!-- Sync Settings Tab (Initially Hidden) -->
2388 + <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
2389 +<div class="mxchat-card">
2390 + <!-- Auto-Sync Settings -->
2391 + <div class="mxchat-settings-section">
2392 + <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
2393 + <p class="mxchat-description">
2394 + <?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'); ?>
2395 + </p>
2396 + <div class="mxchat-autosave-section">
2397 + <div class="mxchat-toggle-group">
2398 + <div class="mxchat-toggle-container">
2399 + <label class="mxchat-toggle-switch">
2400 + <input type="checkbox"
2401 + name="mxchat_auto_sync_posts"
2402 + class="mxchat-autosave-field"
2403 + value="1"
2404 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2405 + <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
2406 + <span class="mxchat-toggle-slider"></span>
2407 + </label>
2408 + <span class="mxchat-toggle-label">
2409 + <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
2410 + </span>
2411 + </div>
2014 2412
2015 -public function get_sitemap_processing_status($sitemap_url) {
2016 - $sitemap_url = esc_url_raw($sitemap_url);
2017 - $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2413 + <div class="mxchat-toggle-container">
2414 + <label class="mxchat-toggle-switch">
2415 + <input type="checkbox"
2416 + name="mxchat_auto_sync_pages"
2417 + class="mxchat-autosave-field"
2418 + value="1"
2419 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2420 + <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
2421 + <span class="mxchat-toggle-slider"></span>
2422 + </label>
2423 + <span class="mxchat-toggle-label">
2424 + <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
2425 + </span>
2426 + </div>
2018 2427
2019 - if (!$status || !is_array($status)) {
2020 - return false;
2021 - }
2428 + <!-- Custom Post Types Section -->
2429 + <div class="mxchat-section-content">
2430 + <div class="mxchat-custom-post-types-header">
2431 + <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
2432 + <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
2433 + <span class="mxchat-toggle-icon">▼</span>
2434 + </button>
2435 + </div>
2022 2436
2023 - return array(
2024 - 'total_urls' => absint($status['total_urls']),
2025 - 'processed_urls' => absint($status['processed_urls']),
2026 - 'failed_urls' => absint($status['failed_urls'] ?? 0),
2027 - 'percentage' => ($status['total_urls'] > 0)
2028 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2029 - : 0,
2030 - 'status' => sanitize_text_field($status['status']),
2031 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2032 - 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2033 - 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
2034 - );
2035 -}
2437 + <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
2438 + <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
2439 + <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
2036 2440
2037 -public function mxchat_stop_processing() {
2038 - // Verify permissions
2039 - if (!current_user_can('manage_options')) {
2040 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
2041 - }
2441 + <div class="mxchat-custom-post-types">
2442 + <?php
2443 + $post_types = $knowledge_manager->mxchat_get_public_post_types();
2444 + // Skip post and page as they're handled separately
2445 + unset($post_types['post']);
2446 + unset($post_types['page']);
2042 2447
2043 - // Verify nonce
2044 - check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
2448 + if (!empty($post_types)) {
2449 + foreach ($post_types as $post_type => $label) {
2450 + $option_name = 'mxchat_auto_sync_' . $post_type;
2451 + $is_enabled = get_option($option_name, '0');
2452 + ?>
2453 + <div class="mxchat-toggle-container">
2454 + <label class="mxchat-toggle-switch">
2455 + <input type="checkbox"
2456 + name="<?php echo esc_attr($option_name); ?>"
2457 + class="mxchat-autosave-field"
2458 + value="1"
2459 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2460 + <?php checked($is_enabled, '1'); ?>>
2461 + <span class="mxchat-toggle-slider"></span>
2462 + </label>
2463 + <span class="mxchat-toggle-label">
2464 + <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
2465 + </span>
2466 + </div>
2467 + <?php
2468 + }
2469 + } else {
2470 + echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
2471 + }
2472 + ?>
2473 + </div>
2474 + </div>
2475 + </div>
2476 + </div>
2477 + </div>
2478 + </div>
2479 +</div>
2480 +</div>
2045 2481
2046 - // Get the last sitemap URL and clear its transient
2047 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
2048 - if ($sitemap_url) {
2049 - delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
2050 - delete_transient('mxchat_last_sitemap_url');
2051 - }
2482 +<!-- NEW PINECONE TAB -->
2483 + <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
2484 + <div class="mxchat-card">
2485 + <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
2486 + <p class="mxchat-description">
2487 + <?php echo wp_kses(
2488 + __('<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'),
2489 + array('strong' => array())
2490 + ); ?>
2491 + </p>
2052 2492
2053 - // Get the last PDF URL and clear its transient
2054 - $pdf_url = get_transient('mxchat_last_pdf_url');
2055 - if ($pdf_url) {
2056 - delete_transient('mxchat_pdf_status_' . md5($pdf_url));
2057 - delete_transient('mxchat_last_pdf_url');
2058 - }
2493 + <div class="mxchat-pinecone-info-box">
2494 + <div class="mxchat-info-icon">
2495 + <span class="dashicons dashicons-info"></span>
2496 + </div>
2497 + <div class="mxchat-info-content">
2498 + <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
2499 + <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>
2500 + <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
2501 + </div>
2502 + </div>
2059 2503
2060 - // Unschedule any pending sitemap events
2061 - $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
2062 - if ($timestamp) {
2063 - wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
2064 - }
2504 + <div class="mxchat-database-settings-form">
2505 + <!-- REMOVED the WordPress form - we're using AJAX auto-save instead -->
2506 + <?php
2507 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
2508 + $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
2509 + ?>
2065 2510
2066 - // Redirect back with a success message
2067 - set_transient('mxchat_admin_notice_success',
2068 - esc_html__('Processing has been stopped successfully.', 'mxchat'),
2069 - 30
2070 - );
2071 - wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2072 - exit;
2073 -}
2074 -private function mxchat_sanitize_content_for_api($content) {
2075 - // Remove script, style tags, and HTML comments
2076 - $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2077 - $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2078 - $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2511 + <div class="mxchat-toggle-container">
2512 + <label class="mxchat-toggle-switch">
2513 + <input type="checkbox"
2514 + name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
2515 + value="1"
2516 + <?php checked($use_pinecone, '1'); ?>>
2517 + <span class="mxchat-toggle-slider"></span>
2518 + </label>
2519 + <span class="mxchat-toggle-label">
2520 + <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
2521 + </span>
2522 + </div>
2079 2523
2080 - // Remove all HTML tags and decode HTML entities
2081 - $content = wp_strip_all_tags($content);
2082 - $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2524 + <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
2083 2525
2084 - // Trim and normalize whitespace
2085 - $content = trim(preg_replace('/\s+/', ' ', $content));
2526 + <?php if ($use_pinecone) : ?>
2527 + <div class="mxchat-knowledge-warning success">
2528 + <p><span class="dashicons dashicons-yes-alt"></span>
2529 + <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
2530 + </p>
2531 + </div>
2532 + <?php endif; ?>
2086 2533
2087 - return $content;
2088 -}
2534 + <div class="mxchat-form-group">
2535 + <label for="mxchat_pinecone_api_key">
2536 + <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
2537 + </label>
2538 + <input type="password"
2539 + id="mxchat_pinecone_api_key"
2540 + name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
2541 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
2542 + class="regular-text"
2543 + placeholder="pcsk_..." />
2544 + <p class="description">
2545 + <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
2546 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
2547 + </p>
2548 + </div>
2089 2549
2550 + <div class="mxchat-form-group">
2551 + <label for="mxchat_pinecone_environment">
2552 + <?php esc_html_e('Region', 'mxchat'); ?>
2553 + </label>
2554 + <input type="text"
2555 + id="mxchat_pinecone_environment"
2556 + name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
2557 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
2558 + placeholder="e.g., gcp-starter"
2559 + class="regular-text" />
2560 + <p class="description">
2561 + <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
2562 + </p>
2563 + </div>
2090 2564
2565 + <div class="mxchat-form-group">
2566 + <label for="mxchat_pinecone_index">
2567 + <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
2568 + </label>
2569 + <input type="text"
2570 + id="mxchat_pinecone_index"
2571 + name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
2572 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
2573 + placeholder="e.g., my-wordpress-vectors"
2574 + class="regular-text" />
2575 + <p class="description">
2576 + <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
2577 + </p>
2578 + </div>
2091 2579
2580 + <div class="mxchat-form-group">
2581 + <label for="mxchat_pinecone_host">
2582 + <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
2583 + </label>
2584 + <input type="text"
2585 + id="mxchat_pinecone_host"
2586 + name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
2587 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
2588 + placeholder="e.g., my-index-xyz123.svc.pinecone.io"
2589 + class="regular-text" />
2590 + <p class="description">
2591 + <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
2592 + </p>
2593 + </div>
2092 2594
2093 -public function mxchat_handle_product_change($post_id, $post, $update) {
2094 - if ($post->post_type !== 'product') {
2095 - return;
2096 - }
2595 +<div class="mxchat-setup-steps">
2596 + <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
2597 + <div class="mxchat-setup-step">
2598 + <span class="step-number">1</span>
2599 + <div class="step-content">
2600 + <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
2601 + <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
2602 + </div>
2603 + </div>
2604 + <div class="mxchat-setup-step">
2605 + <span class="step-number">2</span>
2606 + <div class="step-content">
2607 + <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
2608 + <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
2609 + <ul>
2610 + <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>
2611 + <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
2612 + <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
2613 + </ul>
2614 + </div>
2615 + </div>
2616 + <div class="mxchat-setup-step">
2617 + <span class="step-number">3</span>
2618 + <div class="step-content">
2619 + <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
2620 + <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
2621 + </div>
2622 + </div>
2623 + <div class="mxchat-setup-step">
2624 + <span class="step-number">4</span>
2625 + <div class="step-content">
2626 + <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
2627 + <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
2628 + </div>
2629 + </div>
2630 + <div class="mxchat-setup-step">
2631 + <span class="step-number">5</span>
2632 + <div class="step-content">
2633 + <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
2634 + <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
2635 + </div>
2636 + </div>
2637 +</div>
2097 2638
2098 - if ($post->post_status === 'publish') {
2099 - add_action('shutdown', function() use ($post_id) {
2100 - $product = wc_get_product($post_id);
2101 - if ($product) {
2102 - $this->mxchat_store_product_embedding($product);
2103 - }
2104 - });
2105 - }
2106 -}
2107 2639
2108 -private function mxchat_store_product_embedding($product) {
2109 - if (!isset($this->options['enable_woocommerce_integration']) ||
2110 - !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2111 - return;
2112 - }
2113 2640
2114 - $source_url = get_permalink($product->get_id());
2641 + </div>
2115 2642
2116 - // Build description
2117 - $regular_price = $product->get_regular_price();
2118 - $sale_price = $product->get_sale_price();
2119 - $description = $product->get_description() . "\n\n" .
2120 - esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" .
2121 - esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" .
2122 - esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" .
2123 - esc_html__('SKU:', 'mxchat') . " " . $product->get_sku();
2124 2643
2125 - // Generate embedding vector
2126 - $embedding_vector = $this->mxchat_generate_embedding($description);
2127 - if (!is_array($embedding_vector)) {
2128 - return;
2129 - }
2644 + </div>
2645 + </div>
2646 + </div>
2130 2647
2131 - // Check for Pinecone addon and its settings
2132 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2133 - $use_pinecone = false;
2648 +</div>
2134 2649
2135 - if ($pinecone_settings && is_array($pinecone_settings)) {
2136 - $use_pinecone = (
2137 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2138 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2139 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2140 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2141 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2142 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2143 - );
2144 - }
2145 2650
2146 - if ($use_pinecone) {
2147 - $result = $this->store_in_pinecone_main(
2148 - $embedding_vector,
2149 - $description,
2150 - $source_url,
2151 - $pinecone_settings['mxchat_pinecone_api_key'],
2152 - $pinecone_settings['mxchat_pinecone_environment'],
2153 - $pinecone_settings['mxchat_pinecone_index']
2154 - );
2155 2651
2156 - if (!$result['success']) {
2157 - // Fallback to WordPress DB if Pinecone storage fails
2158 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2159 - }
2160 - } else {
2161 - // Use WordPress DB storage
2162 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2163 - }
2164 -}
2165 2652
2166 -public function mxchat_handle_product_delete($post_id) {
2167 - if (get_post_type($post_id) !== 'product') {
2168 - return;
2169 - }
2653 +</div>
2654 +</div>
2170 2655
2171 - $source_url = get_permalink($post_id);
2172 2656
2173 - // Check for Pinecone addon and its settings
2174 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2175 - $use_pinecone = false;
2657 +<!-- Content Selector Modal -->
2658 +<div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
2659 + <div class="mxchat-kb-modal-content">
2660 +<div class="mxchat-kb-modal-header">
2661 + <h3>
2662 + <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
2663 + <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
2664 + </h3>
2665 + <span class="mxchat-kb-modal-close">&times;</span>
2666 +</div>
2667 + <div class="mxchat-kb-modal-filters">
2668 + <div class="mxchat-kb-search-group">
2669 + <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
2670 + </div>
2176 2671
2177 - if ($pinecone_settings && is_array($pinecone_settings)) {
2178 - $use_pinecone = (
2179 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2180 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2181 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2182 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2183 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2184 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2185 - );
2186 - }
2672 + <div class="mxchat-kb-filter-group">
2673 + <select id="mxchat-kb-content-type-filter">
2674 + <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2675 + <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
2676 + <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
2677 + <?php
2678 + // Add other post types dynamically
2679 + $post_types = get_post_types(array('public' => true), 'objects');
2680 + foreach ($post_types as $post_type) {
2681 + // Skip post and page as they're already added
2682 + if (!in_array($post_type->name, array('post', 'page'))) {
2683 + echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
2684 + }
2685 + }
2686 + ?>
2687 + </select>
2187 2688
2188 - if ($use_pinecone) {
2189 - $this->delete_from_pinecone(
2190 - array($source_url),
2191 - $pinecone_settings['mxchat_pinecone_api_key'],
2192 - $pinecone_settings['mxchat_pinecone_environment'],
2193 - $pinecone_settings['mxchat_pinecone_index']
2194 - );
2195 - } else {
2196 - // Fallback to WordPress DB deletion
2197 - global $wpdb;
2198 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2199 - $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2200 - }
2201 -}
2689 + <select id="mxchat-kb-content-status-filter">
2690 + <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
2691 + <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
2692 + <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
2693 + </select>
2202 2694
2695 + <select id="mxchat-kb-processed-filter">
2696 + <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
2697 + <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
2698 + <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
2699 + </select>
2700 + </div>
2701 + </div>
2203 2702
2204 -/**
2205 - * Delete vectors from Pinecone by source URL
2206 - *
2207 - * @param array $urls Array of source URLs to delete
2208 - * @param string $api_key Pinecone API key
2209 - * @param string $environment Pinecone environment
2210 - * @param string $index_name Pinecone index name
2211 - * @return array Associative array with 'success' boolean and 'message' string
2212 - */
2213 -private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2214 - // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2215 - $options = get_option('mxchat_pinecone_addon_options');
2216 - $host = $options['mxchat_pinecone_host'] ?? '';
2703 + <div class="mxchat-kb-content-selection">
2704 + <div class="mxchat-kb-selection-header">
2705 + <label>
2706 + <input type="checkbox" id="mxchat-kb-select-all">
2707 + <?php esc_html_e('Select All', 'mxchat'); ?>
2708 + </label>
2709 + <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
2710 + </div>
2217 2711
2218 - if (empty($host)) {
2219 - return array(
2220 - 'success' => false,
2221 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2222 - );
2223 - }
2712 + <div class="mxchat-kb-content-list">
2713 + <!-- Content will be loaded here via AJAX -->
2714 + <div class="mxchat-kb-loading">
2715 + <span class="mxchat-kb-spinner is-active"></span>
2716 + <?php esc_html_e('Loading content...', 'mxchat'); ?>
2717 + </div>
2718 + </div>
2224 2719
2225 - // Build API endpoint using the configured host
2226 - $api_endpoint = "https://{$host}/vectors/delete";
2720 + <div class="mxchat-kb-pagination">
2721 + <!-- Pagination will be added here -->
2722 + </div>
2723 + </div>
2227 2724
2228 - // Create vector IDs from URLs (matching your store method's ID generation)
2229 - $vector_ids = array_map('md5', $urls);
2725 + <div class="mxchat-kb-modal-footer">
2726 + <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
2727 + <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
2728 + <span class="mxchat-kb-selected-count">(0)</span>
2729 + </button>
2730 + <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
2731 + <?php esc_html_e('Cancel', 'mxchat'); ?>
2732 + </button>
2733 + </div>
2734 + </div>
2735 +</div>
2230 2736
2231 - // Prepare the delete request body
2232 - $request_body = array(
2233 - 'ids' => $vector_ids,
2234 - 'filter' => array(
2235 - 'source_url' => array(
2236 - '$in' => $urls
2237 - )
2238 - )
2239 - );
2737 +<?php
2738 +}
2240 2739
2241 - // Make the deletion request
2242 - $response = wp_remote_post($api_endpoint, array(
2243 - 'headers' => array(
2244 - 'Api-Key' => $api_key,
2245 - 'accept' => 'application/json',
2246 - 'content-type' => 'application/json'
2247 - ),
2248 - 'body' => wp_json_encode($request_body),
2249 - 'timeout' => 30,
2250 - 'data_format' => 'body'
2251 - ));
2252 -
2253 - // Handle WordPress HTTP API errors
2254 - if (is_wp_error($response)) {
2255 - return array(
2256 - 'success' => false,
2257 - 'message' => $response->get_error_message()
2740 +/**
2741 + * Get bot-specific Pinecone configuration
2742 + * Used in the knowledge retrieval functions
2743 + */
2744 +private function get_bot_pinecone_config($bot_id = 'default') {
2745 + error_log("MXCHAT DEBUG: get_bot_pinecone_config called for bot: " . $bot_id);
2746 +
2747 + // If default bot or multi-bot add-on not active, use default Pinecone config
2748 + if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
2749 + error_log("MXCHAT DEBUG: Using default Pinecone config (no multi-bot or bot is 'default')");
2750 + $addon_options = get_option('mxchat_pinecone_addon_options', array());
2751 + $config = array(
2752 + 'use_pinecone' => (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1'),
2753 + 'api_key' => $addon_options['mxchat_pinecone_api_key'] ?? '',
2754 + 'host' => $addon_options['mxchat_pinecone_host'] ?? '',
2755 + 'namespace' => $addon_options['mxchat_pinecone_namespace'] ?? ''
2258 2756 );
2757 + error_log("MXCHAT DEBUG: Default config - use_pinecone: " . ($config['use_pinecone'] ? 'true' : 'false'));
2758 + return $config;
2259 2759 }
2260 -
2261 - // Check response status
2262 - $response_code = wp_remote_retrieve_response_code($response);
2263 - if ($response_code !== 200) {
2264 - $body = wp_remote_retrieve_body($response);
2265 - return array(
2266 - 'success' => false,
2267 - 'message' => sprintf(
2268 - 'Pinecone API error (HTTP %d): %s',
2269 - $response_code,
2270 - $body
2271 - )
2272 - );
2760 +
2761 + error_log("MXCHAT DEBUG: Calling filter 'mxchat_get_bot_pinecone_config' for bot: " . $bot_id);
2762 +
2763 + // Hook for multi-bot add-on to provide bot-specific Pinecone config
2764 + $bot_pinecone_config = apply_filters('mxchat_get_bot_pinecone_config', array(), $bot_id);
2765 +
2766 + if (!empty($bot_pinecone_config)) {
2767 + error_log("MXCHAT DEBUG: Got bot-specific config from filter");
2768 + error_log(" - use_pinecone: " . (isset($bot_pinecone_config['use_pinecone']) ? ($bot_pinecone_config['use_pinecone'] ? 'true' : 'false') : 'not set'));
2769 + error_log(" - host: " . ($bot_pinecone_config['host'] ?? 'not set'));
2770 + error_log(" - namespace: " . ($bot_pinecone_config['namespace'] ?? 'not set'));
2771 + } else {
2772 + error_log("MXCHAT DEBUG: Filter returned empty config!");
2273 2773 }
2774 +
2775 + return is_array($bot_pinecone_config) ? $bot_pinecone_config : array();
2776 +}
2274 2777
2275 - // Parse response body
2276 - $body = wp_remote_retrieve_body($response);
2277 - $response_data = json_decode($body, true);
2278 -
2279 - // Final validation of the response
2280 - if (json_last_error() !== JSON_ERROR_NONE) {
2281 - return array(
2282 - 'success' => false,
2283 - 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2284 - );
2778 +public function mxchat_delete_chat_history() {
2779 + if (!current_user_can('manage_options')) {
2780 + echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
2781 + wp_die();
2285 2782 }
2286 -
2287 - return array(
2288 - 'success' => true,
2289 - 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2290 - );
2291 -}
2292 -
2293 -
2294 -public function mxchat_fetch_chat_history() {
2783 + check_ajax_referer('mxchat_delete_chat_history', 'security');
2295 2784 global $wpdb;
2296 2785 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2297 2786
2298 - if (!current_user_can('manage_options')) {
2299 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2300 - }
2787 + if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
2788 + $deleted_count = 0;
2301 2789
2302 - // First get unique session IDs ordered by most recent message in each session
2303 - $session_ids = $wpdb->get_col(
2304 - $wpdb->prepare(
2305 - "SELECT DISTINCT session_id
2306 - FROM {$table_name}
2307 - GROUP BY session_id
2308 - ORDER BY MAX(timestamp) DESC"
2309 - )
2310 - );
2790 + foreach ($_POST['delete_session_ids'] as $session_id) {
2791 + $session_id_sanitized = sanitize_text_field($session_id);
2311 2792
2312 - if (empty($session_ids)) {
2313 - wp_die(esc_html__('No chat history available.', 'mxchat'));
2314 - }
2793 + // Clear relevant cache before deletion
2794 + $cache_key = 'chat_session_' . $session_id_sanitized;
2795 + wp_cache_delete($cache_key, 'mxchat_chat_sessions');
2315 2796
2316 - ob_start();
2317 - echo '<div class="mxchat-transcript">';
2797 + // Perform the deletion from the database table
2798 + $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
2318 2799
2319 - // Iterate through sessions from newest to oldest
2320 - foreach ($session_ids as $session_id) {
2321 - // Get the email associated with this session (if available)
2322 - $email = $wpdb->get_var(
2323 - $wpdb->prepare(
2324 - "SELECT user_email
2325 - FROM {$table_name}
2326 - WHERE session_id = %s AND user_email != ''
2327 - ORDER BY timestamp ASC
2328 - LIMIT 1",
2329 - $session_id
2330 - )
2331 - );
2800 + // Delete the corresponding option entry from wp_options table
2801 + delete_option("mxchat_history_" . $session_id_sanitized);
2332 2802
2333 - // Debug: Check email retrieval
2334 - if (empty($email)) {
2335 - //error_log("No email found for session {$session_id}");
2336 - } else {
2337 - //error_log("Email for session {$session_id}: " . $email);
2803 + // Delete any associated metadata options
2804 + delete_option("mxchat_email_" . $session_id_sanitized);
2805 + delete_option("mxchat_agent_name_" . $session_id_sanitized);
2806 +
2807 + $deleted_count++;
2338 2808 }
2339 2809
2340 - // Get messages for this session ordered by timestamp
2341 - $messages = $wpdb->get_results(
2342 - $wpdb->prepare(
2343 - "SELECT * FROM {$table_name}
2344 - WHERE session_id = %s
2345 - ORDER BY timestamp ASC",
2346 - $session_id
2810 + // Optionally, clear a general cache if you have one
2811 + wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
2812 +
2813 + echo wp_json_encode([
2814 + 'success' => sprintf(
2815 + esc_html__('%d chat session(s) have been deleted from all storage locations.', 'mxchat'),
2816 + $deleted_count
2347 2817 )
2348 - );
2818 + ]);
2819 + } else {
2820 + echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
2821 + }
2349 2822
2350 - // Start session block
2351 - echo '<div class="mxchat-session">';
2352 - echo '<div class="mxchat-session-header">';
2353 - // Wrap checkbox and session ID in one block
2354 - echo '<div class="mxchat-session-id">';
2355 - echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
2356 - echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
2357 - echo '</div>';
2823 + wp_die();
2824 +}
2825 +public function display_admin_notices() {
2826 + // Check if we're on a MXChat admin page
2827 + $screen = get_current_screen();
2828 + if (!$screen || strpos($screen->base, 'mxchat') === false) {
2829 + return;
2830 + }
2358 2831
2359 - // Place email directly below the session ID block
2360 - if (!empty($email)) {
2361 - echo '<div class="mxchat-session-email">';
2362 - echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
2363 - echo '</div>';
2364 - }
2365 - echo '</div>';
2832 + //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
2366 2833
2367 - echo '<div class="mxchat-messages">';
2834 + // Check for error notices
2835 + $error_notice = get_transient('mxchat_admin_notice_error');
2836 + if ($error_notice) {
2837 + //error_log('Found error transient: ' . $error_notice);
2838 + echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
2839 + delete_transient('mxchat_admin_notice_error');
2840 + //error_log('Displayed and deleted error transient');
2841 + } else {
2842 + //error_log('No error transient found');
2843 + }
2368 2844
2369 - // Display messages for this session
2370 - foreach ($messages as $transcript) {
2371 - $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
2845 + // Check for success notices
2846 + $success_notice = get_transient('mxchat_admin_notice_success');
2847 + if ($success_notice) {
2848 + //error_log('Found success transient: ' . $success_notice);
2849 + echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
2850 + delete_transient('mxchat_admin_notice_success');
2851 + //error_log('Displayed and deleted success transient');
2852 + }
2372 2853
2373 - // Determine message styling
2374 - switch ($transcript->role) {
2375 - case 'assistant':
2376 - case 'bot':
2377 - $message_class = 'bot-message';
2378 - $display_role = esc_html__('Chatbot', 'mxchat');
2379 - break;
2380 - case 'user':
2381 - $message_class = 'user-message';
2382 - $display_role = !empty($transcript->user_identifier)
2383 - ? sanitize_text_field($transcript->user_identifier)
2384 - : esc_html__('User', 'mxchat');
2385 - break;
2386 - case 'agent':
2387 - $message_class = 'agent-message';
2388 - $display_role = esc_html__('Agent', 'mxchat');
2389 - break;
2390 - default:
2391 - $message_class = 'unknown-message';
2392 - $display_role = esc_html__('Unknown', 'mxchat');
2393 - }
2394 -
2395 - // Process message content
2396 - $message_content = wp_kses(
2397 - stripslashes($transcript->message),
2398 - [
2399 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2400 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2401 - 'a' => ['href' => [], 'title' => []]
2402 - ]
2403 - );
2404 - $message_content = nl2br($message_content);
2405 -
2406 - // Render message
2407 - echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
2408 - echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
2409 - echo '<div class="mxchat-message-content">' . $message_content . '</div>';
2410 - echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
2411 - echo '</div>';
2412 - }
2413 -
2414 - // Close session block
2415 - echo '</div></div>';
2854 + // Check for info notices
2855 + $info_notice = get_transient('mxchat_admin_notice_info');
2856 + if ($info_notice) {
2857 + //error_log('Found info transient: ' . $info_notice);
2858 + echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
2859 + delete_transient('mxchat_admin_notice_info');
2860 + //error_log('Displayed and deleted info transient');
2416 2861 }
2417 2862
2418 - echo '</div>';
2419 - $output = ob_get_clean();
2420 - echo $output;
2421 - wp_die();
2422 2863 }
2423 2864
2424 2865
2425 2866 public function mxchat_create_activation_page() {
@@ -2424,16 +2865,26 @@
2424 2865
2425 2866 public function mxchat_create_activation_page() {
2426 2867 $license_status = get_option('mxchat_license_status', 'inactive');
2427 2868 $license_error = get_option('mxchat_license_error', '');
2869 + $current_domain = parse_url(home_url(), PHP_URL_HOST);
2870 +
2871 + $license_management_url = 'https://mxchat.ai/my-account/orders/';
2872 +
2873 + // Check if this activation has been linked to a domain
2874 + $is_domain_linked = $this->is_current_activation_linked($current_domain);
2428 2875 ?>
2429 2876 <div class="wrap mxchat-admin-activation">
2430 2877 <div class="mxchat-pro-hero">
2431 2878 <h1 class="pro-title">
2432 - <span class="pro-gradient-text">Activate</span> MxChat Pro
2879 + <span class="pro-gradient-text">Manage</span> MxChat Pro
2433 2880 </h1>
2434 2881 <p class="pro-subtitle">
2435 - <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2882 + <?php if ($license_status === 'active'): ?>
2883 + <?php esc_html_e('Your pro license is active. Manage your activation below.', 'mxchat'); ?>
2884 + <?php else: ?>
2885 + <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2886 + <?php endif; ?>
2436 2887 </p>
2437 2888 </div>
2438 2889
2439 2890 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
@@ -2441,8 +2892,25 @@
2441 2892 <p><?php echo esc_html($license_error); ?></p>
2442 2893 </div>
2443 2894 <?php endif; ?>
2444 2895
2896 + <!-- Domain Linking Notice for Existing Users -->
2897 + <?php if ($license_status === 'active' && !$is_domain_linked): ?>
2898 + <div class="notice notice-info">
2899 + <p>
2900 + <strong><?php esc_html_e('New Feature: Domain Tracking', 'mxchat'); ?></strong><br>
2901 + <?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'); ?>
2902 + </p>
2903 + <p>
2904 + <button type="button" id="link-domain-button" class="button button-secondary">
2905 + <?php esc_html_e('Link This Domain', 'mxchat'); ?>
2906 + </button>
2907 + <span id="domain-link-status" style="margin-left: 10px;"></span>
2908 + </p>
2909 + </div>
2910 + <?php endif; ?>
2911 +
2912 + <!-- Activation Form -->
2445 2913 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2446 2914 <div class="mxchat-pro-form-container">
2447 2915 <table class="form-table">
2448 2916 <tr valign="top">
@@ -2457,8 +2925,12 @@
2457 2925 <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 />
2458 2926 </td>
2459 2927 </tr>
2460 2928 </table>
2929 +
2930 + <!-- Hidden field for domain -->
2931 + <input type="hidden" id="mxchat_domain" name="domain" value="<?php echo esc_attr($current_domain); ?>" />
2932 +
2461 2933 <?php if ($license_status !== 'active'): ?>
2462 2934 <div class="mxchat-pro-button-container">
2463 2935 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2464 2936 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
@@ -2465,8 +2937,9 @@
2465 2937 </div>
2466 2938 <?php endif; ?>
2467 2939 </div>
2468 2940 </form>
2941 +
2469 2942 <!-- License Status Display -->
2470 2943 <div class="mxchat-pro-status">
2471 2944 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2472 2945 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
@@ -2472,15 +2945,91 @@
2472 2945 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2473 2946 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2474 2947 </span>
2475 2948 </h3>
2949 +
2950 + <?php if ($license_status === 'active'): ?>
2951 + <div class="mxchat-license-details">
2952 + <p><strong><?php esc_html_e('Domain:', 'mxchat'); ?></strong> <?php echo esc_html($current_domain); ?>
2953 + <?php if ($is_domain_linked): ?>
2954 + <span style="color: green; margin-left: 10px;">✓ <?php esc_html_e('Linked', 'mxchat'); ?></span>
2955 + <?php else: ?>
2956 + <span style="color: orange; margin-left: 10px;">⚠ <?php esc_html_e('Not Linked', 'mxchat'); ?></span>
2957 + <?php endif; ?>
2958 + </p>
2959 + <p><strong><?php esc_html_e('Email:', 'mxchat'); ?></strong> <?php echo esc_html(get_option('mxchat_pro_email')); ?></p>
2960 + <p><strong><?php esc_html_e('License Key:', 'mxchat'); ?></strong>
2961 + <code style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px;">
2962 + <?php echo esc_html(get_option('mxchat_activation_key')); ?>
2963 + </code>
2964 + </p>
2965 + <p>
2966 + <small>
2967 + <?php esc_html_e('Manage all your licenses and domains on', 'mxchat'); ?>
2968 + <a href="<?php echo esc_url($license_management_url); ?>" target="_blank">
2969 + <?php esc_html_e('your account dashboard', 'mxchat'); ?>
2970 + </a>
2971 + </small>
2972 + </p>
2973 +
2974 + <!-- Deactivation Section -->
2975 + <div class="mxchat-deactivation-section" style="margin-top: 20px; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
2976 + <h4 style="margin-top: 0; color: #856404;">
2977 + <?php esc_html_e('License Management', 'mxchat'); ?>
2978 + </h4>
2979 + <p style="margin-bottom: 15px; color: #856404;">
2980 + <?php esc_html_e('Need to move this license to another site? Deactivate it here first, then activate it on your new site.', 'mxchat'); ?>
2981 + </p>
2982 + <button type="button" id="deactivate-license-button" class="button button-secondary" style="background: #dc3545; color: white; border-color: #dc3545;">
2983 + 🔓 <?php esc_html_e('Deactivate License', 'mxchat'); ?>
2984 + </button>
2985 + <span id="deactivate-status" style="margin-left: 10px;"></span>
2986 + </div>
2987 + </div>
2988 + <?php endif; ?>
2476 2989 </div>
2477 2990
2991 + <!-- Hidden fields for JavaScript -->
2992 + <input type="hidden" id="mxchat-ajax-url" value="<?php echo admin_url('admin-ajax.php'); ?>" />
2993 + <input type="hidden" id="mxchat-nonce" value="<?php echo wp_create_nonce('mxchat_activate_license_nonce'); ?>" />
2994 + <input type="hidden" id="mxchat-api-url" value="https://mxchat.ai" />
2478 2995 </div>
2479 2996 <?php
2480 2997 }
2481 2998
2482 -public function mxchat_intents_page_html() {
2999 +/**
3000 + * Check if current activation is linked to a domain
3001 + * This checks YOUR website's database, not the user's local database
3002 + */
3003 +private function is_current_activation_linked($domain) {
3004 + $license_key = get_option('mxchat_activation_key');
3005 + $email = get_option('mxchat_pro_email');
3006 +
3007 + if (empty($license_key) || empty($email)) {
3008 + return false;
3009 + }
3010 +
3011 + // Check with YOUR website's API
3012 + $response = wp_remote_post('https://mxchat.ai/mxchat-api/check-domain', array(
3013 + 'body' => array(
3014 + 'license_key' => $license_key,
3015 + 'email' => $email,
3016 + 'domain' => $domain
3017 + ),
3018 + 'timeout' => 10,
3019 + 'sslverify' => false
3020 + ));
3021 +
3022 + if (is_wp_error($response)) {
3023 + return false;
3024 + }
3025 +
3026 + $body = json_decode(wp_remote_retrieve_body($response), true);
3027 + return isset($body['success']) && $body['success'] && isset($body['data']['linked']) && $body['data']['linked'];
3028 +}
3029 +
3030 +
3031 +public function mxchat_actions_page_html() {
2483 3032 if (!current_user_can('manage_options')) {
2484 3033 return;
2485 3034 }
2486 3035
@@ -2493,9 +3042,9 @@
2493 3042
2494 3043 // Success message
2495 3044 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2496 3045 echo '<div class="notice notice-success is-dismissible"><p>' .
2497 - esc_html__('Intent updated successfully.', 'mxchat') .
3046 + esc_html__('Action updated successfully.', 'mxchat') .
2498 3047 '</p></div>';
2499 3048 }
2500 3049
2501 3050 // Filtering logic
@@ -2516,10 +3065,10 @@
2516 3065 // Pagination
2517 3066 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2518 3067 $total_pages = ceil($total_intents / $per_page);
2519 3068
2520 - // Get intents
2521 - $intents = $wpdb->get_results($wpdb->prepare(
3069 + // Get intents (now called actions)
3070 + $actions = $wpdb->get_results($wpdb->prepare(
2522 3071 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2523 3072 $per_page, $offset
2524 3073 ));
2525 3074
@@ -2530,605 +3079,1104 @@
2530 3079 <div class="wrap mxchat-wrapper">
2531 3080 <!-- Hero Section -->
2532 3081 <div class="mxchat-hero">
2533 3082 <h1 class="mxchat-main-title">
2534 - <span class="mxchat-gradient-text">Intent</span> Manager
3083 + <span class="mxchat-gradient-text">Actions</span> Manager
2535 3084 </h1>
2536 3085 <p class="mxchat-hero-subtitle">
2537 - <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
3086 + <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
2538 3087 </p>
2539 3088 </div>
2540 3089
2541 - <div class="mxchat-content">
2542 - <!-- Add Intent Card -->
2543 - <div class="mxchat-card">
2544 - <div class="mxchat-card-header">
2545 - <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2546 - </div>
2547 -
2548 - <div class="mxchat-intent-documentation">
2549 - <p>
2550 - <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2551 - <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2552 - <?php esc_html_e('documentation', 'mxchat'); ?>
2553 - </a>
2554 - <?php esc_html_e(' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat'); ?>
2555 - </p>
2556 - </div>
2557 -
2558 - <form id="mxchat-add-intent-form" method="post"
2559 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2560 - <input type="hidden" name="action" value="mxchat_add_intent">
2561 - <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2562 -
2563 - <div class="mxchat-form-group">
2564 - <label for="intent_label">
2565 - <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2566 - </label>
2567 - <input name="intent_label" type="text" id="intent_label" required
2568 - class="mxchat-intent-input"
2569 - placeholder="<?php esc_attr_e('Example Email Capture: Newsletter Signup', 'mxchat'); ?>">
3090 + <!-- Actions Header with Search and Filter -->
3091 + <div class="mxchat-actions-header">
3092 + <div class="mxchat-actions-filters">
3093 + <form method="get" class="mxchat-search-form">
3094 + <input type="hidden" name="page" value="mxchat-actions">
3095 + <div class="mxchat-search-group">
3096 + <span class="dashicons dashicons-search"></span>
3097 + <input type="text" name="s" class="mxchat-search-input"
3098 + placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
3099 + value="<?php echo esc_attr($search_term); ?>">
2570 3100 </div>
2571 -
2572 - <div class="mxchat-form-group">
2573 - <label for="phrases">
2574 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2575 - </label>
2576 - <textarea name="phrases" id="phrases" rows="5" required
2577 - class="mxchat-intent-textarea"
2578 - placeholder="<?php esc_attr_e('Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed', 'mxchat'); ?>"></textarea>
2579 - </div>
2580 -
2581 - <div class="mxchat-form-group">
2582 - <label for="callback_function">
2583 - <?php esc_html_e('Callback Function', 'mxchat'); ?>
2584 - </label>
2585 - <select name="callback_function" id="callback_function"
2586 - class="mxchat-intent-select" required>
2587 - <option value="">
2588 - <?php esc_html_e('Select a Callback', 'mxchat'); ?>
3101 + <select name="callback_filter" class="mxchat-action-filter">
3102 + <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
3103 + <?php foreach ($available_callbacks as $function => $callback_data) :
3104 + $label = $callback_data['label']; ?>
3105 + <option value="<?php echo esc_attr($function); ?>"
3106 + <?php selected($callback_filter, $function); ?>>
3107 + <?php echo esc_html($label); ?>
2589 3108 </option>
2590 - <?php
2591 - $groups = $this->mxchat_get_available_callbacks(true);
2592 - foreach ($groups as $group_label => $group_callbacks) :
2593 - echo '<optgroup label="' . esc_attr($group_label) . '">';
2594 - foreach ($group_callbacks as $function => $data) :
2595 - $label = $data['label'];
2596 - $pro_only = $data['pro_only'];
2597 - $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2598 - $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2599 - ?>
2600 - <option value="<?php echo esc_attr($function); ?>"
2601 - <?php echo $disabled; ?>>
2602 - <?php echo esc_html($label . $label_suffix); ?>
2603 - </option>
2604 - <?php
2605 - endforeach;
2606 - echo '</optgroup>';
2607 - endforeach;
2608 - ?>
2609 - </select>
2610 - </div>
2611 -
2612 - <button type="submit" class="mxchat-button-primary">
2613 - <?php esc_html_e('Add Intent', 'mxchat'); ?>
3109 + <?php endforeach; ?>
3110 + </select>
3111 + <button type="submit" class="mxchat-button-secondary">
3112 + <?php esc_html_e('Filter', 'mxchat'); ?>
2614 3113 </button>
2615 3114 </form>
2616 3115 </div>
3116 + <div class="mxchat-actions-controls">
3117 + <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
3118 + <span class="dashicons dashicons-plus-alt"></span>
3119 + <?php esc_html_e('Add New Action', 'mxchat'); ?>
3120 + </button>
3121 + </div>
3122 + </div>
2617 3123
2618 - <!-- Manage Intents Card -->
2619 - <div class="mxchat-card">
2620 - <div class="mxchat-card-header">
2621 - <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2622 - <div class="mxchat-header-actions">
2623 - <form method="get" class="mxchat-search-form">
2624 - <input type="hidden" name="page" value="mxchat-intents">
2625 - <div class="mxchat-search-group">
2626 - <span class="dashicons dashicons-search"></span>
2627 - <input type="text" name="s"
2628 - placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2629 - value="<?php echo esc_attr($search_term); ?>">
2630 - <select name="callback_filter" class="mxchat-intent-filter">
2631 - <option value="">
2632 - <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2633 - </option>
2634 - <?php foreach ($available_callbacks as $function => $callback_data) :
2635 - $label = $callback_data['label']; ?>
2636 - <option value="<?php echo esc_attr($function); ?>"
2637 - <?php selected($callback_filter, $function); ?>>
2638 - <?php echo esc_html($label); ?>
2639 - </option>
2640 - <?php endforeach; ?>
2641 - </select>
2642 - <button type="submit" class="mxchat-button-secondary">
2643 - <?php esc_html_e('Filter', 'mxchat'); ?>
2644 - </button>
3124 + <!-- Actions Grid Layout - All actions in a single grid -->
3125 + <div class="mxchat-actions-grid">
3126 + <div class="mxchat-cards-container">
3127 + <?php if (!empty($actions)) : ?>
3128 + <?php foreach ($actions as $action) :
3129 + $callback_function = $action->callback_function;
3130 + $callback_label = isset($available_callbacks[$callback_function]['label'])
3131 + ? $available_callbacks[$callback_function]['label']
3132 + : $callback_function;
3133 + $threshold_value = isset($action->similarity_threshold)
3134 + ? round($action->similarity_threshold * 100)
3135 + : 85;
3136 +
3137 + // Check if this is a form action
3138 + $is_form_action = strpos($action->intent_label, 'Form ') === 0;
3139 +
3140 + // Get action status (enabled/disabled) - default to true if column doesn't exist
3141 + $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
3142 +
3143 + // Get enabled bots for display
3144 + $enabled_bots = [];
3145 + if (isset($action->enabled_bots) && !empty($action->enabled_bots)) {
3146 + $enabled_bots = json_decode($action->enabled_bots, true);
3147 + if (!is_array($enabled_bots)) {
3148 + $enabled_bots = ['default'];
3149 + }
3150 + } else {
3151 + $enabled_bots = ['default']; // Backward compatibility
3152 + }
3153 + ?>
3154 + <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
3155 + <div class="mxchat-card-header">
3156 + <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
3157 + <div class="mxchat-card-toggle">
3158 + <label class="mxchat-switch">
3159 + <input type="checkbox" class="mxchat-action-toggle"
3160 + data-action-id="<?php echo esc_attr($action->id); ?>"
3161 + <?php checked($is_enabled); ?>>
3162 + <span class="mxchat-slider round"></span>
3163 + </label>
3164 + </div>
2645 3165 </div>
2646 - </form>
2647 - </div>
2648 - </div>
2649 3166
2650 - <div class="mxchat-table-wrapper">
2651 - <table class="mxchat-records-table">
2652 - <thead>
2653 - <tr>
2654 - <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2655 - <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2656 - <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2657 - <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2658 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2659 - </tr>
2660 - </thead>
2661 - <tbody>
2662 - <?php if ($intents) :
2663 - foreach ($intents as $intent) :
2664 - $callback_function = $intent->callback_function;
2665 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2666 - ? $available_callbacks[$callback_function]['label']
2667 - : $callback_function;
2668 - $threshold_value = isset($intent->similarity_threshold)
2669 - ? round($intent->similarity_threshold * 100)
2670 - : 85;
2671 -
2672 - // Check if this is a form intent (its intent_label starts with "Form ")
2673 - $is_form_intent = strpos($intent->intent_label, 'Form ') === 0;
3167 + <div class="mxchat-card-body">
3168 + <div class="mxchat-card-description">
3169 + <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
3170 + <?php echo esc_html($callback_label); ?>
3171 + </div>
3172 +
3173 + <div class="mxchat-card-phrases">
3174 + <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
3175 + <div class="mxchat-phrases-preview">
3176 + <?php
3177 + // Check if the helper function exists, otherwise use a simple substring
3178 + if (method_exists($this, 'get_trimmed_phrases')) {
3179 + echo esc_html($this->get_trimmed_phrases($action->phrases));
3180 + } else {
3181 + echo esc_html(strlen($action->phrases) > 100 ?
3182 + substr($action->phrases, 0, 97) . '...' :
3183 + $action->phrases);
3184 + }
2674 3185 ?>
2675 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2676 - <td>
2677 - <?php
2678 - if ($is_form_intent) {
2679 - // Attempt to extract the form ID from the intent_label.
2680 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2681 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2682 - if ($form_id) {
2683 - global $wpdb;
2684 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2685 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2686 - if ($form && !empty($form->title)) {
2687 - echo esc_html($form->title);
2688 - } else {
2689 - echo esc_html($intent->intent_label);
2690 - }
2691 - } else {
2692 - echo esc_html($intent->intent_label);
2693 - }
2694 - echo ' <span class="mxchat-badge">Form Intent</span>';
3186 + </div>
3187 + </div>
3188 +
3189 + <div class="mxchat-threshold-control">
3190 + <div class="mxchat-threshold-label">
3191 + <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
3192 + <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
3193 + </div>
3194 + </div>
3195 +
3196 + <!-- Bot Availability Display (Read-only) -->
3197 + <div class="mxchat-card-bots">
3198 + <strong><?php esc_html_e('Assigned bots:', 'mxchat'); ?></strong>
3199 + <div class="mxchat-bot-badges">
3200 + <?php
3201 + foreach ($enabled_bots as $bot_id) {
3202 + if ($bot_id === 'default') {
3203 + echo '<span class="mxchat-bot-badge">' . esc_html__('Default Bot', 'mxchat') . '</span>';
3204 + } else {
3205 + // Try to get bot name from multi-bot manager
3206 + if (class_exists('MxChat_Multi_Bot_Core_Manager')) {
3207 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
3208 + $available_bots = $multi_bot_manager->get_available_bots();
3209 + $bot_name = isset($available_bots[$bot_id]) ? $available_bots[$bot_id] : $bot_id;
3210 + echo '<span class="mxchat-bot-badge">' . esc_html($bot_name) . '</span>';
2695 3211 } else {
2696 - echo esc_html($intent->intent_label);
3212 + echo '<span class="mxchat-bot-badge">' . esc_html($bot_id) . '</span>';
2697 3213 }
2698 - ?>
2699 - </td>
2700 - <td class="mxchat-content-cell">
2701 - <?php echo esc_html($intent->phrases); ?>
2702 - </td>
2703 - <td><?php echo esc_html($callback_label); ?></td>
2704 - <td>
2705 - <!-- Similarity threshold adjustment (shown for all intents) -->
2706 - <form method="post"
2707 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2708 - class="mxchat-threshold-form">
2709 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2710 - <input type="hidden" name="action"
2711 - value="mxchat_update_intent_threshold">
2712 - <input type="hidden" name="intent_id"
2713 - value="<?php echo esc_attr($intent->id); ?>">
2714 - <div class="mxchat-slider-group">
2715 - <input type="range"
2716 - name="intent_threshold"
2717 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2718 - min="70"
2719 - max="95"
2720 - value="<?php echo esc_attr($threshold_value); ?>"
2721 - class="mxchat-intent-slider"
2722 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2723 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2724 - class="mxchat-intent-output">
2725 - <?php echo esc_html($threshold_value); ?>%
2726 - </output>
2727 - </div>
2728 - <button type="submit" class="mxchat-button-icon">
2729 - <span class="dashicons dashicons-saved"></span>
2730 - </button>
2731 - </form>
2732 - </td>
2733 - <td class="mxchat-actions-cell">
2734 - <?php if (!$is_form_intent) : ?>
2735 - <!-- Standard intents: allow edit and delete -->
2736 - <button type="button"
2737 - class="mxchat-button-icon mxchat-edit-button"
2738 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2739 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2740 - <span class="dashicons dashicons-edit"></span>
2741 - </button>
2742 - <form method="post"
2743 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2744 - class="mxchat-delete-form"
2745 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2746 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2747 - <input type="hidden" name="action" value="mxchat_delete_intent">
2748 - <input type="hidden" name="intent_id"
2749 - value="<?php echo esc_attr($intent->id); ?>">
2750 - <button type="submit" class="mxchat-button-icon">
2751 - <span class="dashicons dashicons-trash"></span>
2752 - </button>
2753 - </form>
2754 - <?php else : ?>
2755 - <!-- Form intents: show manage in forms button -->
2756 - <?php
2757 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2758 - $form_id = isset($matches[1]) ? $matches[1] : '';
2759 - ?>
2760 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2761 - class="mxchat-button-secondary">
2762 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2763 - </a>
2764 - <?php endif; ?>
2765 - </td>
2766 - </tr>
2767 - <?php endforeach;
2768 - else : ?>
2769 - <tr>
2770 - <td colspan="5" class="mxchat-no-records">
2771 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2772 - </td>
2773 - </tr>
2774 - <?php endif; ?>
2775 - </tbody>
2776 - </table>
2777 - </div>
3214 + }
3215 + }
3216 + ?>
3217 + </div>
3218 + </div>
3219 + </div>
2778 3220
2779 - <?php if ($total_pages > 1) : ?>
2780 - <div class="mxchat-pagination">
2781 - <?php
2782 - echo paginate_links(array(
2783 - 'base' => add_query_arg('paged', '%#%'),
2784 - 'format' => '',
2785 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
2786 - 'next_text' => __('Next &raquo;', 'mxchat'),
2787 - 'total' => $total_pages,
2788 - 'current' => $page
2789 - ));
2790 - ?>
3221 + <div class="mxchat-card-footer">
3222 + <?php
3223 + // Check if it's a form action
3224 + $is_form_action = preg_match('/Form (\d+)/', $action->intent_label, $form_matches);
3225 +
3226 + // Check if it's a recommendation flow action
3227 + $is_flow_action = preg_match('/Recommendation Flow (\d+)/', $action->intent_label, $flow_matches);
3228 +
3229 + if ($is_form_action) {
3230 + $form_id = isset($form_matches[1]) ? $form_matches[1] : '';
3231 + ?>
3232 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
3233 + class="mxchat-button-primary">
3234 + <span class="dashicons dashicons-feedback"></span>
3235 + <?php esc_html_e('Edit Form', 'mxchat'); ?>
3236 + </a>
3237 + <?php } elseif ($is_flow_action) {
3238 + ?>
3239 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-smart-recommender')); ?>"
3240 + class="mxchat-button-primary">
3241 + <span class="dashicons dashicons-list-view"></span>
3242 + <?php esc_html_e('Manage Flows', 'mxchat'); ?>
3243 + </a>
3244 + <?php } else { ?>
3245 + <button type="button"
3246 + class="mxchat-button-secondary mxchat-edit-button"
3247 + data-action-id="<?php echo esc_attr($action->id); ?>"
3248 + data-phrases="<?php echo esc_attr($action->phrases); ?>"
3249 + data-label="<?php echo esc_attr($action->intent_label); ?>"
3250 + data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
3251 + data-callback-function="<?php echo esc_attr($action->callback_function); ?>"
3252 + data-enabled-bots="<?php echo esc_attr(json_encode($enabled_bots)); ?>">
3253 + <span class="dashicons dashicons-edit"></span>
3254 + <?php esc_html_e('Edit', 'mxchat'); ?>
3255 + </button>
3256 + <form method="post"
3257 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
3258 + class="mxchat-delete-form"
3259 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
3260 + <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
3261 + <input type="hidden" name="action" value="mxchat_delete_intent">
3262 + <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
3263 + <button type="submit" class="mxchat-button-text mxchat-delete-button">
3264 + <span class="dashicons dashicons-trash"></span>
3265 + <?php esc_html_e('Delete', 'mxchat'); ?>
3266 + </button>
3267 + </form>
3268 + <?php } ?>
3269 + </div>
3270 + </div>
3271 + <?php endforeach; ?>
3272 + <?php else : ?>
3273 + <!-- If no actions found -->
3274 + <div class="mxchat-no-actions">
3275 + <div class="mxchat-empty-state">
3276 + <span class="dashicons dashicons-format-chat"></span>
3277 + <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
3278 + <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
3279 + <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
3280 + <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
3281 + </button>
3282 + </div>
2791 3283 </div>
2792 3284 <?php endif; ?>
2793 3285 </div>
2794 3286 </div>
2795 3287
2796 - <!-- Edit Modal -->
2797 -<div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
3288 + <?php if ($total_pages > 1) : ?>
3289 + <div class="mxchat-pagination">
3290 + <?php
3291 + echo paginate_links(array(
3292 + 'base' => add_query_arg('paged', '%#%'),
3293 + 'format' => '',
3294 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
3295 + 'next_text' => __('Next &raquo;', 'mxchat'),
3296 + 'total' => $total_pages,
3297 + 'current' => $page
3298 + ));
3299 + ?>
3300 + </div>
3301 + <?php endif; ?>
3302 +
3303 +<!-- Add/Edit Action Modal with Step-Based Approach -->
3304 +<!-- Complete Modal HTML with Defined Groups Variable -->
3305 +<div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
2798 3306 <div class="mxchat-modal-content">
2799 3307 <span class="mxchat-modal-close">&times;</span>
2800 - <div class="mxchat-card-header">
2801 - <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2802 - </div>
2803 3308
2804 - <form id="mxchat-edit-intent-form" method="post"
2805 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2806 - <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2807 - <input type="hidden" name="action" value="mxchat_edit_intent">
2808 - <input type="hidden" name="intent_id" id="edit_intent_id">
3309 + <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3310 + <!-- Dynamic nonce field -->
3311 + <div id="action-nonce-container">
3312 + <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
3313 + </div>
3314 + <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
3315 + <input type="hidden" name="intent_id" id="edit_action_id" value="">
3316 + <input type="hidden" name="callback_function" id="callback_function" value="">
2809 3317
2810 - <div class="mxchat-form-group">
2811 - <label for="edit_phrases">
2812 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2813 - </label>
2814 - <textarea name="phrases"
2815 - id="edit_phrases"
2816 - rows="5"
2817 - class="mxchat-intent-textarea"
2818 - required></textarea>
3318 + <!-- Step 1: Action Type Selection -->
3319 + <div id="mxchat-action-step-1" class="mxchat-action-step active">
3320 + <div class="mxchat-step-indicator">
3321 + <div class="mxchat-step-number">1</div>
3322 + <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
3323 + </div>
3324 +
3325 + <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
3326 + <div class="mxchat-action-type-search">
3327 + <span class="dashicons dashicons-search"></span>
3328 + <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
3329 + </div>
3330 +
3331 + <?php
3332 + // Get the callbacks - IMPORTANT: Define the $groups variable here
3333 + $groups = $this->mxchat_get_available_callbacks(true, true);
3334 + ?>
3335 +
3336 + <div class="mxchat-action-type-categories">
3337 + <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
3338 + <?php
3339 + // Get unique categories from the defined groups
3340 + foreach ($groups as $group_label => $group_callbacks) :
3341 + $category_slug = sanitize_title($group_label);
3342 + ?>
3343 + <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
3344 + <?php endforeach; ?>
3345 + </div>
3346 +
3347 + <div class="mxchat-action-types-grid">
3348 + <?php
3349 + // Generate action cards from available callbacks
3350 + foreach ($groups as $group_label => $group_callbacks) :
3351 + $category_slug = sanitize_title($group_label);
3352 +
3353 + foreach ($group_callbacks as $function => $data) :
3354 + $label = $data['label'];
3355 + $pro_only = $data['pro_only'];
3356 + $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
3357 + $description = isset($data['description']) ? $data['description'] : '';
3358 + $is_addon = isset($data['addon']) && $data['addon'] !== false;
3359 + $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
3360 + $is_installed = isset($data['installed']) ? $data['installed'] : true;
3361 +
3362 + // Determine card status and styling
3363 + $card_class = 'mxchat-action-type-card';
3364 + $icon_class = 'mxchat-action-type-icon';
3365 + $status_badge = '';
3366 +
3367 + if ($pro_only && !$this->is_activated) {
3368 + // Pro feature but no Pro license
3369 + $icon_class .= ' pro-feature';
3370 + $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
3371 + }
3372 +
3373 + if ($is_addon && !$is_installed) {
3374 + // Add-on not installed
3375 + $card_class .= ' not-installed';
3376 + $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3377 + }
3378 +
3379 + // Default description if none provided
3380 + if (empty($description)) {
3381 + $description = sprintf(
3382 + esc_html__('Use the %s action in your chatbot', 'mxchat'),
3383 + $label
3384 + );
3385 + }
3386 + ?>
3387 + <div class="<?php echo esc_attr($card_class); ?>"
3388 + data-category="<?php echo esc_attr($category_slug); ?>"
3389 + data-value="<?php echo esc_attr($function); ?>"
3390 + data-label="<?php echo esc_attr($label); ?>"
3391 + data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
3392 + data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
3393 + data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
3394 + <div class="<?php echo esc_attr($icon_class); ?>">
3395 + <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
3396 + </div>
3397 + <div class="mxchat-action-type-info">
3398 + <h4><?php echo esc_html($label); ?></h4>
3399 + <p><?php echo esc_html($description); ?></p>
3400 + <?php if (!empty($status_badge)) : ?>
3401 + <?php echo $status_badge; ?>
3402 + <?php endif; ?>
3403 +
3404 + <?php if ($is_addon && !$is_installed) : ?>
3405 + <div class="mxchat-addon-info">
3406 + <?php echo esc_html(sprintf(
3407 + __('Requires %s', 'mxchat'),
3408 + $addon_name
3409 + )); ?>
3410 + </div>
3411 + <?php endif; ?>
3412 + </div>
3413 + </div>
3414 + <?php endforeach;
3415 + endforeach; ?>
3416 + </div>
3417 + </div>
3418 +
3419 + <div class="mxchat-modal-actions">
3420 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3421 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3422 + </button>
3423 + </div>
2819 3424 </div>
2820 3425
2821 - <div class="mxchat-modal-actions">
2822 - <button type="submit" class="mxchat-button-primary">
2823 - <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2824 - </button>
2825 - <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2826 - <?php esc_html_e('Cancel', 'mxchat'); ?>
2827 - </button>
3426 + <!-- Step 2: Action Configuration -->
3427 + <div id="mxchat-action-step-2" class="mxchat-action-step">
3428 + <div class="mxchat-step-indicator">
3429 + <div class="mxchat-step-number">2</div>
3430 + <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
3431 + </div>
3432 +
3433 + <div class="mxchat-selected-action">
3434 + <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
3435 + <span class="dashicons dashicons-arrow-left-alt"></span>
3436 + <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
3437 + </button>
3438 + <div class="mxchat-selected-action-info">
3439 + <div id="selected-action-icon" class="mxchat-action-type-icon">
3440 + <span class="dashicons dashicons-admin-generic"></span>
3441 + </div>
3442 + <div class="mxchat-selected-action-details">
3443 + <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
3444 + <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
3445 + </div>
3446 + </div>
3447 + </div>
3448 +
3449 + <div class="mxchat-form-group">
3450 + <label for="intent_label">
3451 + <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
3452 + </label>
3453 + <input name="intent_label" type="text" id="intent_label" required
3454 + class="mxchat-intent-input"
3455 + placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
3456 + </div>
3457 +
3458 + <div class="mxchat-form-group">
3459 + <label for="phrases">
3460 + <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
3461 + </label>
3462 + <textarea name="phrases" id="action_phrases" rows="5" required
3463 + class="mxchat-intent-textarea"
3464 + placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
3465 + </div>
3466 +
3467 + <div class="mxchat-form-group">
3468 + <label for="similarity_threshold">
3469 + <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
3470 + <span class="mxchat-threshold-value-display">85%</span>
3471 + </label>
3472 + <div class="mxchat-slider-group modal-slider">
3473 + <input type="range"
3474 + name="similarity_threshold"
3475 + id="similarity_threshold"
3476 + min="10"
3477 + max="95"
3478 + value="85"
3479 + class="mxchat-intent-slider"
3480 + oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
3481 + </div>
3482 + <div class="mxchat-threshold-hint">
3483 + <?php esc_html_e('Lower values (10-30) make the action trigger more easily. Higher values (70-95) require more exact matches.', 'mxchat'); ?>
3484 + </div>
3485 + </div>
3486 +
3487 + <!-- Bot Selection Section -->
3488 + <div class="mxchat-form-group">
3489 + <label for="enabled_bots">
3490 + <?php esc_html_e('Which bots should we enable this action for?', 'mxchat'); ?>
3491 + </label>
3492 + <div class="mxchat-bot-selector">
3493 + <div class="mxchat-bot-option">
3494 + <label class="mxchat-checkbox-label">
3495 + <input type="checkbox"
3496 + name="enabled_bots[]"
3497 + value="default"
3498 + id="bot_default"
3499 + checked="checked">
3500 + <span class="mxchat-checkmark"></span>
3501 + <?php esc_html_e('Default Bot', 'mxchat'); ?>
3502 + </label>
3503 + </div>
3504 +
3505 + <?php if (class_exists('MxChat_Multi_Bot_Core_Manager')) :
3506 + $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
3507 + $available_bots = $multi_bot_manager->get_available_bots();
3508 +
3509 + foreach ($available_bots as $bot_id => $bot_name) :
3510 + if ($bot_id === 'default') continue; // Skip default, already shown above
3511 + ?>
3512 + <div class="mxchat-bot-option">
3513 + <label class="mxchat-checkbox-label">
3514 + <input type="checkbox"
3515 + name="enabled_bots[]"
3516 + value="<?php echo esc_attr($bot_id); ?>"
3517 + id="bot_<?php echo esc_attr($bot_id); ?>">
3518 + <span class="mxchat-checkmark"></span>
3519 + <?php echo esc_html($bot_name); ?>
3520 + </label>
3521 + </div>
3522 + <?php
3523 + endforeach;
3524 + endif; ?>
3525 + </div>
3526 + </div>
3527 +
3528 + <div class="mxchat-modal-actions">
3529 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3530 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3531 + </button>
3532 + <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
3533 + <?php esc_html_e('Save Action', 'mxchat'); ?>
3534 + </button>
3535 + </div>
2828 3536 </div>
2829 3537 </form>
2830 3538 </div>
2831 3539 </div>
2832 3540
2833 - </div><!-- .mxchat-content -->
3541 +<div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
3542 + <div class="mxchat-action-loading-spinner"></div>
3543 + <div class="mxchat-action-loading-text">
3544 + <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
3545 + </div>
3546 +</div>
2834 3547 </div><!-- .mxchat-wrapper -->
3548 + <?php
3549 +}
3550 +private function get_trimmed_phrases($phrases, $max_length = 100) {
3551 + if (strlen($phrases) <= $max_length) {
3552 + return $phrases;
3553 + }
2835 3554
2836 - <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
2837 - <div class="mxchat-intent-loading-spinner"></div>
2838 - <div class="mxchat-intent-loading-text">
2839 - <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
2840 - </div>
2841 - </div>
2842 -<?php
3555 + $trimmed = substr($phrases, 0, $max_length);
3556 + $last_comma = strrpos($trimmed, ',');
3557 +
3558 + if ($last_comma !== false) {
3559 + $trimmed = substr($trimmed, 0, $last_comma);
3560 + }
3561 +
3562 + return $trimmed . '...';
2843 3563 }
3564 +public function mxchat_add_enabled_column_to_intents() {
3565 + global $wpdb;
3566 + $table_name = $wpdb->prefix . 'mxchat_intents';
2844 3567
3568 + // Check if the column already exists
3569 + $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
2845 3570
3571 + if (empty($columns)) {
3572 + // Add the column with default value of 1 (enabled)
3573 + $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
3574 + }
3575 +}
2846 3576
2847 -/**
2848 - * Handle editing of intent phrases
2849 - *
2850 - * @since 1.0.0
2851 - * @return void
2852 - */
2853 -public function handle_edit_intent() {
2854 - // Verify nonce and user capabilities
2855 - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
2856 - wp_die(esc_html__('Security check failed.', 'mxchat'));
3577 +public function mxchat_handle_delete_intent() {
3578 + if ( ! current_user_can( 'manage_options' ) ) {
3579 + wp_die( esc_html__('Unauthorized user', 'mxchat') );
2857 3580 }
2858 3581
3582 + check_admin_referer('mxchat_delete_intent_nonce');
3583 +
3584 + if (isset($_POST['intent_id'])) {
3585 + global $wpdb;
3586 + $table_name = $wpdb->prefix . 'mxchat_intents';
3587 + $intent_id = intval($_POST['intent_id']);
3588 +
3589 + $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3590 + }
3591 +
3592 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3593 + exit;
3594 +}
3595 +public function mxchat_handle_edit_intent() {
3596 + // Security checks (nonce and permissions)
2859 3597 if (!current_user_can('manage_options')) {
2860 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
3598 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2861 3599 }
3600 + check_admin_referer('mxchat_edit_intent');
2862 3601
2863 - // Validate and sanitize input
3602 + // Get POST data
2864 3603 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2865 - if (!$intent_id) {
2866 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
3604 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
3605 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
3606 + $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
3607 + $similarity_threshold = min(95, max(10, $threshold_percentage)) / 100; // Convert to 0.10–0.95
3608 +
3609 + // NEW: Handle enabled_bots
3610 + $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
3611 + $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
3612 +
3613 + // Ensure default is always included for backward compatibility
3614 + if (!in_array('default', $enabled_bots)) {
3615 + $enabled_bots[] = 'default';
2867 3616 }
3617 +
3618 + $enabled_bots_json = json_encode($enabled_bots);
2868 3619
2869 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2870 - if (empty($phrases_input)) {
2871 - wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
3620 + // Validate inputs
3621 + if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
3622 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
3623 + return;
2872 3624 }
2873 3625
2874 - // Process phrases the same way as in intent creation
2875 3626 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2876 -
2877 3627 if (empty($phrases_array)) {
2878 - wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
3628 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
3629 + return;
2879 3630 }
2880 3631
2881 - // Generate embeddings and combine them
3632 + // Generate embeddings with improved error handling
2882 3633 $vectors = [];
3634 + $failed_phrases = [];
3635 +
2883 3636 foreach ($phrases_array as $phrase) {
2884 3637 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2885 3638 if (is_array($embedding_vector)) {
2886 3639 $vectors[] = $embedding_vector;
2887 3640 } else {
2888 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
3641 + $failed_phrases[] = $phrase;
2889 3642 }
2890 3643 }
2891 3644
3645 + if (!empty($failed_phrases)) {
3646 + $this->handle_embedding_error(
3647 + sprintf(
3648 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
3649 + implode(', ', $failed_phrases)
3650 + )
3651 + );
3652 + return;
3653 + }
3654 +
2892 3655 if (empty($vectors)) {
2893 - wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3656 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3657 + return;
2894 3658 }
2895 3659
2896 - // Create combined vector just like in intent creation
2897 3660 $combined_vector = $this->mxchat_average_vectors($vectors);
2898 3661 $serialized_vector = maybe_serialize($combined_vector);
2899 3662
3663 + // Update the database
2900 3664 global $wpdb;
2901 3665 $table_name = $wpdb->prefix . 'mxchat_intents';
2902 3666
2903 - // Update the intent with both new phrases and the combined vector
2904 3667 $result = $wpdb->update(
2905 3668 $table_name,
2906 3669 array(
3670 + 'intent_label' => $intent_label,
2907 3671 'phrases' => implode(', ', $phrases_array),
2908 - 'embedding_vector' => $serialized_vector
3672 + 'embedding_vector' => $serialized_vector,
3673 + 'similarity_threshold' => $similarity_threshold,
3674 + 'enabled_bots' => $enabled_bots_json // NEW: Include enabled_bots in update
2909 3675 ),
2910 3676 array('id' => $intent_id),
2911 - array('%s', '%s'),
2912 - array('%d')
3677 + array('%s', '%s', '%s', '%f', '%s'), // Format: string, string, string, float, string
3678 + array('%d') // Where format: integer
2913 3679 );
2914 3680
2915 3681 if (false === $result) {
2916 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
3682 + $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
3683 + return;
2917 3684 }
2918 3685
2919 - // Redirect back to the intents page with a success message
3686 + // Set success message and redirect
3687 + set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
3688 +
2920 3689 $redirect_url = add_query_arg(
2921 3690 array(
2922 - 'page' => 'mxchat-intents',
2923 - 'updated' => 'true'
3691 + 'page' => 'mxchat-actions'
2924 3692 ),
2925 3693 admin_url('admin.php')
2926 3694 );
2927 -
2928 3695 wp_safe_redirect($redirect_url);
2929 3696 exit;
2930 3697 }
2931 -public function mxchat_handle_update_intent_threshold() {
2932 - if ( ! current_user_can( 'manage_options' ) ) {
2933 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
2934 - }
2935 -
2936 - check_admin_referer('mxchat_update_intent_threshold_nonce');
2937 -
2938 - if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
2939 - global $wpdb;
2940 - $table_name = $wpdb->prefix . 'mxchat_intents';
2941 - $intent_id = intval($_POST['intent_id']);
2942 - $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
2943 - $similarity_threshold = $threshold_percentage / 100;
2944 -
2945 - $wpdb->update(
2946 - $table_name,
2947 - ['similarity_threshold' => $similarity_threshold],
2948 - ['id' => $intent_id],
2949 - ['%f'],
2950 - ['%d']
2951 - );
2952 - }
2953 -
2954 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2955 - exit;
2956 -}
2957 -
2958 3698 public function mxchat_handle_add_intent() {
2959 - if ( ! current_user_can( 'manage_options' ) ) {
2960 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3699 + if (!current_user_can('manage_options')) {
3700 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
2961 3701 }
2962 -
2963 3702 check_admin_referer('mxchat_add_intent_nonce');
2964 -
2965 3703 global $wpdb;
2966 3704 $table_name = $wpdb->prefix . 'mxchat_intents';
2967 -
3705 +
3706 + // Sanitize and get form data
2968 3707 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
2969 3708 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2970 3709 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2971 - $default_threshold = 0.85;
2972 -
3710 +
3711 + // Get similarity threshold from form (convert percentage to decimal)
3712 + $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
3713 +
3714 + // NEW: Handle enabled_bots
3715 + $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
3716 + $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
3717 +
3718 + // Ensure default is always included for backward compatibility with existing actions
3719 + if (!in_array('default', $enabled_bots)) {
3720 + $enabled_bots[] = 'default';
3721 + }
3722 +
3723 + $enabled_bots_json = json_encode($enabled_bots);
3724 +
3725 + // Validate required fields
2973 3726 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
2974 - wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
3727 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
3728 + return;
2975 3729 }
2976 -
3730 +
3731 + // Validate callback function
2977 3732 $available_callbacks = $this->mxchat_get_available_callbacks();
2978 -
2979 3733 if (!array_key_exists($callback_function, $available_callbacks)) {
2980 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
3734 + $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
3735 + return;
2981 3736 }
2982 -
3737 +
3738 + // Check Pro requirements
2983 3739 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2984 3740 if ($is_pro_only && !$this->is_activated) {
2985 - wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
3741 + $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
3742 + return;
2986 3743 }
2987 -
3744 +
3745 + // Process phrases
2988 3746 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2989 -
2990 3747 if (empty($phrases_array)) {
2991 - wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
3748 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
3749 + return;
2992 3750 }
2993 -
3751 +
3752 + // Generate embeddings with improved error handling
2994 3753 $vectors = [];
3754 + $failed_phrases = [];
2995 3755 foreach ($phrases_array as $phrase) {
2996 3756 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2997 3757 if (is_array($embedding_vector)) {
2998 3758 $vectors[] = $embedding_vector;
2999 3759 } else {
3000 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
3760 + $failed_phrases[] = $phrase;
3001 3761 }
3002 3762 }
3763 +
3764 + // Check for embedding failures
3765 + if (!empty($failed_phrases)) {
3766 + $this->handle_embedding_error(
3767 + sprintf(
3768 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
3769 + implode(', ', $failed_phrases)
3770 + )
3771 + );
3772 + return;
3773 + }
3774 +
3775 + if (empty($vectors)) {
3776 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3777 + return;
3778 + }
3779 +
3780 + // Create combined vector and insert into database
3781 + $combined_vector = $this->mxchat_average_vectors($vectors);
3782 + $serialized_vector = maybe_serialize($combined_vector);
3783 +
3784 + $result = $wpdb->insert($table_name, [
3785 + 'intent_label' => $intent_label,
3786 + 'phrases' => implode(', ', $phrases_array),
3787 + 'embedding_vector' => $serialized_vector,
3788 + 'callback_function' => $callback_function,
3789 + 'similarity_threshold' => $similarity_threshold,
3790 + 'enabled_bots' => $enabled_bots_json // NEW field
3791 + ]);
3792 +
3793 + if ($result === false) {
3794 + $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
3795 + return;
3796 + }
3797 +
3798 + // Set success message and redirect
3799 + set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
3800 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3801 + exit;
3802 +}
3003 3803
3004 - if (!empty($vectors)) {
3005 - $combined_vector = $this->mxchat_average_vectors($vectors);
3006 - $serialized_vector = maybe_serialize($combined_vector);
3007 3804
3008 - $result = $wpdb->insert($table_name, [
3009 - 'intent_label' => $intent_label,
3010 - 'phrases' => implode(', ', $phrases_array),
3011 - 'embedding_vector' => $serialized_vector,
3012 - 'callback_function' => $callback_function,
3013 - 'similarity_threshold' => $default_threshold,
3014 - ]);
3015 3805
3016 - if ($result === false) {
3017 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3806 +
3807 +private function handle_embedding_error($message, $redirect = true) {
3808 + // Store the error message in the existing transient
3809 + set_transient('mxchat_admin_notice_error', $message, 60);
3810 +
3811 + if ($redirect) {
3812 + // Redirect back to the actions page
3813 + $redirect_url = add_query_arg(
3814 + array(
3815 + 'page' => 'mxchat-actions'
3816 + ),
3817 + admin_url('admin.php')
3818 + );
3819 + wp_safe_redirect($redirect_url);
3820 + exit;
3821 + }
3822 +}
3823 +
3824 +
3825 +/**
3826 + * Enhanced get_available_callbacks function with form action exclusion
3827 + *
3828 + * @param bool $grouped Whether to return callbacks grouped by category
3829 + * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
3830 + * @return array Callbacks data with icons, descriptions and availability status
3831 + */
3832 +private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
3833 + // Load WordPress plugin functions if needed
3834 + if (!function_exists('get_plugins')) {
3835 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
3836 + }
3837 +
3838 + // Get active plugins
3839 + $active_plugins = get_option('active_plugins', array());
3840 +
3841 + // Functions to exclude from the action selector only if Pro is activated
3842 + // If user doesn't have Pro, show these so they can see what they're missing
3843 + $excluded_when_pro_active_functions = array(
3844 + 'mxchat_handle_form_collection', // Forms add-on action
3845 + 'mxchat_sr_recommendation_flow' // Smart Recommender flow actions
3846 + );
3847 +
3848 + // Always excluded functions (regardless of Pro status)
3849 + $always_excluded_functions = array();
3850 +
3851 + // Combine exclusion lists based on Pro activation status
3852 + $excluded_functions = $always_excluded_functions;
3853 + if ($this->is_activated) {
3854 + // Only exclude add-on managed functions if Pro is active
3855 + $excluded_functions = array_merge($excluded_functions, $excluded_when_pro_active_functions);
3856 + }
3857 +
3858 + // Define add-on plugin files and their corresponding action functions
3859 + $addon_plugins = array(
3860 + 'mxchat-woo/mxchat-woo.php' => array(
3861 + 'functions' => array(
3862 + 'mxchat_handle_product_recommendations',
3863 + 'mxchat_handle_order_history',
3864 + 'mxchat_show_product_card',
3865 + 'mxchat_add_to_cart',
3866 + 'mxchat_checkout_redirect'
3867 + ),
3868 + 'name' => __('WooCommerce Add-on', 'mxchat'),
3869 + 'pro_required' => true
3870 + ),
3871 + 'mxchat-perplexity/mxchat-perplexity.php' => array(
3872 + 'functions' => array('mxchat_perplexity_research'),
3873 + 'name' => __('Perplexity Add-on', 'mxchat'),
3874 + 'pro_required' => true
3875 + ),
3876 + 'mxchat-forms/mxchat-forms.php' => array(
3877 + 'functions' => array('mxchat_handle_form_collection'),
3878 + 'name' => __('Forms Add-on', 'mxchat'),
3879 + 'pro_required' => true
3880 + ),
3881 + 'mxchat-smart-recommender/mxchat-smart-recommender.php' => array(
3882 + 'functions' => array('mxchat_sr_recommendation_flow'),
3883 + 'name' => __('Smart Recommender Add-on', 'mxchat'),
3884 + 'pro_required' => true
3885 + ),
3886 + // Add other add-ons and their functions here
3887 + );
3888 +
3889 + // Get the functions that are provided by active add-ons
3890 + $addon_provided_functions = array();
3891 + $addon_function_mapping = array(); // Maps functions to their add-on info
3892 +
3893 + // Check which add-ons are active
3894 + foreach ($addon_plugins as $plugin_file => $addon_info) {
3895 + $is_active = in_array($plugin_file, $active_plugins);
3896 +
3897 + // For each function in this addon
3898 + foreach ($addon_info['functions'] as $function) {
3899 + // Consider a function installed only if:
3900 + // 1. The add-on is active AND
3901 + // 2. Either it doesn't require Pro OR Pro is activated
3902 + $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
3903 +
3904 + // If the add-on is installed, mark this function as provided by an add-on
3905 + if ($is_installed) {
3906 + $addon_provided_functions[] = $function;
3907 + }
3908 +
3909 + // Store addon info for this function regardless of installation status
3910 + $addon_function_mapping[$function] = array(
3911 + 'addon' => basename(dirname($plugin_file)),
3912 + 'addon_name' => $addon_info['name'],
3913 + 'pro_required' => $addon_info['pro_required'],
3914 + 'is_active' => $is_active,
3915 + 'is_installed' => $is_installed
3916 + );
3018 3917 }
3019 - } else {
3020 - wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
3021 3918 }
3022 3919
3023 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3024 - exit;
3025 -}
3920 + // Core callbacks - always available in the base plugin
3921 + $core_callbacks = array(
3922 + 'mxchat_handle_email_capture' => array(
3923 + 'label' => __('Loops Email Capture', 'mxchat'),
3924 + 'pro_only' => false,
3925 + 'group' => __('Customer Engagement', 'mxchat'),
3926 + 'icon' => 'email-alt',
3927 + 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
3928 + 'addon' => false, // Not from an add-on
3929 + 'installed' => true // Always installed with base plugin
3930 + ),
3931 + 'mxchat_handle_search_request' => array(
3932 + 'label' => __('Brave Web Search', 'mxchat'),
3933 + 'pro_only' => false,
3934 + 'group' => __('Search Features', 'mxchat'),
3935 + 'icon' => 'search',
3936 + 'description' => __('Let users search the web directly from the chat', 'mxchat'),
3937 + 'addon' => false,
3938 + 'installed' => true
3939 + ),
3940 + 'mxchat_handle_image_search_request' => array(
3941 + 'label' => __('Brave Image Search', 'mxchat'),
3942 + 'pro_only' => false,
3943 + 'group' => __('Search Features', 'mxchat'),
3944 + 'icon' => 'format-image',
3945 + 'description' => __('Search and display images in the chat conversation', 'mxchat'),
3946 + 'addon' => false,
3947 + 'installed' => true
3948 + ),
3949 + // Pro core features - check is_activated property
3950 + 'mxchat_generate_image' => array(
3951 + 'label' => __('Generate Image', 'mxchat'),
3952 + 'pro_only' => false,
3953 + 'group' => __('Other Features', 'mxchat'),
3954 + 'icon' => 'art',
3955 + 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
3956 + 'addon' => false,
3957 + 'installed' => true
3958 + ),
3959 + 'mxchat_handle_pdf_discussion' => array(
3960 + 'label' => __('Chat with PDF', 'mxchat'),
3961 + 'pro_only' => false,
3962 + 'group' => __('Other Features', 'mxchat'),
3963 + 'icon' => 'media-document',
3964 + 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
3965 + 'addon' => false,
3966 + 'installed' => true
3967 + ),
3968 + 'mxchat_live_agent_handover' => array(
3969 + 'label' => __('Slack Live Agent', 'mxchat'),
3970 + 'pro_only' => false,
3971 + 'group' => __('Customer Engagement', 'mxchat'),
3972 + 'icon' => 'admin-users',
3973 + 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
3974 + 'addon' => false,
3975 + 'installed' => true
3976 + ),
3977 + 'mxchat_handle_switch_to_chatbot_intent' => array(
3978 + 'label' => __('Back to Chatbot', 'mxchat'),
3979 + 'pro_only' => false,
3980 + 'group' => __('Customer Engagement', 'mxchat'),
3981 + 'icon' => 'backup',
3982 + 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
3983 + 'addon' => false,
3984 + 'installed' => true
3985 + ),
3986 + );
3026 3987
3988 + // Add-on callbacks with placeholders - only include if the add-on is NOT active
3989 + $addon_callbacks = array(
3990 + // WooCommerce Add-on
3991 + 'mxchat_handle_product_recommendations' => array(
3992 + 'label' => __('Product Recommendations', 'mxchat'),
3993 + 'pro_only' => true,
3994 + 'group' => __('WooCommerce Features', 'mxchat'),
3995 + 'icon' => 'cart',
3996 + 'description' => __('Suggest products based on customer preferences', 'mxchat'),
3997 + ),
3998 + 'mxchat_handle_order_history' => array(
3999 + 'label' => __('Order History', 'mxchat'),
4000 + 'pro_only' => true,
4001 + 'group' => __('WooCommerce Features', 'mxchat'),
4002 + 'icon' => 'clipboard',
4003 + 'description' => __('Allow customers to check their order status', 'mxchat'),
4004 + ),
4005 + 'mxchat_show_product_card' => array(
4006 + 'label' => __('Show Product Card', 'mxchat'),
4007 + 'pro_only' => true,
4008 + 'group' => __('WooCommerce Features', 'mxchat'),
4009 + 'icon' => 'products',
4010 + 'description' => __('Display product information in the chat', 'mxchat'),
4011 + ),
4012 + 'mxchat_add_to_cart' => array(
4013 + 'label' => __('Add to Cart', 'mxchat'),
4014 + 'pro_only' => true,
4015 + 'group' => __('WooCommerce Features', 'mxchat'),
4016 + 'icon' => 'plus-alt',
4017 + 'description' => __('Add products to cart directly from chat', 'mxchat'),
4018 + ),
4019 + 'mxchat_checkout_redirect' => array(
4020 + 'label' => __('Proceed to Checkout', 'mxchat'),
4021 + 'pro_only' => true,
4022 + 'group' => __('WooCommerce Features', 'mxchat'),
4023 + 'icon' => 'arrow-right-alt',
4024 + 'description' => __('Redirect customer to checkout page', 'mxchat'),
4025 + ),
3027 4026
4027 + // Perplexity Add-on
4028 + 'mxchat_perplexity_research' => array(
4029 + 'label' => __('Perplexity Research', 'mxchat'),
4030 + 'pro_only' => true,
4031 + 'group' => __('Search Features', 'mxchat'),
4032 + 'icon' => 'book-alt',
4033 + 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
4034 + ),
3028 4035
4036 + // Forms Add-on (only shown when Pro is not activated)
4037 + 'mxchat_handle_form_collection' => array(
4038 + 'label' => __('Form Collection', 'mxchat'),
4039 + 'pro_only' => true,
4040 + 'group' => __('Form Features', 'mxchat'),
4041 + 'icon' => 'feedback',
4042 + 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
4043 + ),
3029 4044
4045 + // Smart Recommender Add-on (only shown when Pro is not activated)
4046 + 'mxchat_sr_recommendation_flow' => array(
4047 + 'label' => __('Smart Recommender Flow', 'mxchat'),
4048 + 'pro_only' => true,
4049 + 'group' => __('Recommendation Features', 'mxchat'),
4050 + 'icon' => 'cart',
4051 + 'description' => __('Create interactive conversation flows that collect user preferences and deliver personalized product or service recommendations', 'mxchat'),
4052 + ),
4053 + );
3030 4054
4055 + // Enhance add-on callbacks with installation status and addon info
4056 + foreach ($addon_callbacks as $function => $data) {
4057 + if (isset($addon_function_mapping[$function])) {
4058 + $addon_info = $addon_function_mapping[$function];
3031 4059
3032 -private function mxchat_get_available_callbacks($grouped = false) {
3033 - $callbacks = [
3034 - 'mxchat_handle_email_capture' => [
3035 - 'label' => __('Email Capture', 'mxchat'),
3036 - 'pro_only' => false,
3037 - 'group' => __('Customer Engagement', 'mxchat'),
3038 - ],
3039 - 'mxchat_generate_image' => [
3040 - 'label' => __('Generate Image', 'mxchat'),
3041 - 'pro_only' => true,
3042 - 'group' => __('Other Features', 'mxchat'),
3043 - ],
3044 - 'mxchat_handle_search_request' => [
3045 - 'label' => __('Brave Web Search', 'mxchat'),
3046 - 'pro_only' => false,
3047 - 'group' => __('Search Features', 'mxchat'),
3048 - ],
3049 - 'mxchat_handle_image_search_request' => [
3050 - 'label' => __('Brave Image Search', 'mxchat'),
3051 - 'pro_only' => false,
3052 - 'group' => __('Search Features', 'mxchat'),
3053 - ],
3054 - 'mxchat_handle_pdf_discussion' => [
3055 - 'label' => __('Chat with PDF', 'mxchat'),
3056 - 'pro_only' => true,
3057 - 'group' => __('Other Features', 'mxchat'),
3058 - ],
3059 - 'mxchat_live_agent_handover' => [
3060 - 'label' => __('Live Agent', 'mxchat'),
3061 - 'pro_only' => true,
3062 - 'group' => __('Customer Engagement', 'mxchat'),
3063 - ],
3064 - 'mxchat_handle_switch_to_chatbot_intent' => [
3065 - 'label' => __('Back to Chatbot', 'mxchat'),
3066 - 'pro_only' => true,
3067 - 'group' => __('Customer Engagement', 'mxchat'),
3068 - ],
3069 - ];
4060 + $addon_callbacks[$function]['addon'] = $addon_info['addon'];
4061 + $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4062 + $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
3070 4063
3071 - // Allow other plugins to add their callbacks
3072 - $callbacks = apply_filters('mxchat_available_callbacks', $callbacks);
4064 + // Set pro_only based on add-on configuration
4065 + $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4066 + } else {
4067 + $addon_callbacks[$function]['addon'] = 'unknown';
4068 + $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
4069 + $addon_callbacks[$function]['installed'] = false;
4070 + }
4071 + }
3073 4072
3074 - // Return grouped structure if requested
3075 - if ($grouped) {
3076 - $grouped_callbacks = [];
3077 - foreach ($callbacks as $key => $data) {
3078 - $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3079 - $grouped_callbacks[$group_label][$key] = [
3080 - 'label' => $data['label'],
3081 - 'pro_only' => $data['pro_only'],
3082 - ];
4073 + // Initialize callbacks with core features
4074 + $callbacks = $core_callbacks;
4075 +
4076 + // Get callbacks from active add-ons
4077 + $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
4078 +
4079 + // Add placeholder callbacks only for add-ons that aren't active
4080 + if ($include_all) {
4081 + foreach ($addon_callbacks as $function => $data) {
4082 + // Skip placeholders for functions provided by active add-ons
4083 + if (in_array($function, $addon_provided_functions)) {
4084 + continue;
4085 + }
4086 +
4087 + // Skip excluded functions
4088 + if (in_array($function, $excluded_functions)) {
4089 + continue;
4090 + }
4091 +
4092 + // Add the placeholder
4093 + $callbacks[$function] = $data;
3083 4094 }
3084 - return $grouped_callbacks;
3085 4095 }
3086 - return $callbacks;
3087 -}
3088 4096
4097 + // Add callbacks from active add-ons (will override placeholders)
4098 + foreach ($active_addon_callbacks as $function => $data) {
4099 + // Skip excluded functions
4100 + if (in_array($function, $excluded_functions)) {
4101 + continue;
4102 + }
3089 4103
3090 -private function mxchat_average_vectors($vectors) {
3091 - $vector_length = count($vectors[0]);
3092 - $sum_vector = array_fill(0, $vector_length, 0);
4104 + // Always include callbacks from add-ons
4105 + $callbacks[$function] = $data;
3093 4106
3094 - foreach ($vectors as $vector) {
3095 - for ($i = 0; $i < $vector_length; $i++) {
3096 - $sum_vector[$i] += $vector[$i];
4107 + // Ensure they have the proper add-on info
4108 + if (isset($addon_function_mapping[$function])) {
4109 + $addon_info = $addon_function_mapping[$function];
4110 + $callbacks[$function]['addon'] = $addon_info['addon'];
4111 + $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4112 + $callbacks[$function]['installed'] = $addon_info['is_installed'];
4113 + $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
3097 4114 }
3098 4115 }
3099 4116
3100 - // Divide each component by the number of vectors to get the average
3101 - $num_vectors = count($vectors);
3102 - for ($i = 0; $i < $vector_length; $i++) {
3103 - $sum_vector[$i] /= $num_vectors;
4117 + // Just before returning callbacks, sort them to prioritize free features
4118 + if (!$grouped) {
4119 + // Create temporary arrays for sorting
4120 + $free_callbacks = array();
4121 + $pro_callbacks = array();
4122 +
4123 + // Split callbacks into free and pro
4124 + foreach ($callbacks as $key => $data) {
4125 + if (isset($data['pro_only']) && $data['pro_only']) {
4126 + $pro_callbacks[$key] = $data;
4127 + } else {
4128 + $free_callbacks[$key] = $data;
4129 + }
4130 + }
4131 +
4132 + // Merge with free callbacks first
4133 + $callbacks = array_merge($free_callbacks, $pro_callbacks);
3104 4134 }
3105 4135
3106 - return $sum_vector;
3107 -}
4136 + // Return grouped structure if requested
4137 + if ($grouped) {
4138 + $grouped_callbacks = array();
4139 + foreach ($callbacks as $key => $data) {
4140 + $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
3108 4141
3109 -public function mxchat_handle_delete_intent() {
3110 - if ( ! current_user_can( 'manage_options' ) ) {
3111 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3112 - }
4142 + // Ensure we carry forward all the new fields in grouped mode
4143 + $callback_data = array(
4144 + 'label' => $data['label'],
4145 + 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
4146 + 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
4147 + 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
4148 + 'addon' => isset($data['addon']) ? $data['addon'] : false,
4149 + 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
4150 + 'installed' => isset($data['installed']) ? $data['installed'] : true
4151 + );
3113 4152
3114 - check_admin_referer('mxchat_delete_intent_nonce');
4153 + $grouped_callbacks[$group_label][$key] = $callback_data;
4154 + }
3115 4155
3116 - if (isset($_POST['intent_id'])) {
3117 - global $wpdb;
3118 - $table_name = $wpdb->prefix . 'mxchat_intents';
3119 - $intent_id = intval($_POST['intent_id']);
4156 + // Sort within each group to prioritize free features
4157 + foreach ($grouped_callbacks as $group => $items) {
4158 + $free_items = array();
4159 + $pro_items = array();
3120 4160
3121 - $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
4161 + foreach ($items as $key => $data) {
4162 + if (isset($data['pro_only']) && $data['pro_only']) {
4163 + $pro_items[$key] = $data;
4164 + } else {
4165 + $free_items[$key] = $data;
4166 + }
4167 + }
4168 +
4169 + $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
4170 + }
4171 +
4172 + return $grouped_callbacks;
3122 4173 }
3123 4174
3124 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3125 - exit;
4175 + return $callbacks;
3126 4176 }
3127 4177
3128 -
3129 4178 public function mxchat_page_init() {
3130 - // Register settings
3131 4179 register_setting(
3132 4180 'mxchat_option_group',
3133 4181 'mxchat_options',
3134 4182 array($this, 'mxchat_sanitize')
@@ -3139,10 +4187,10 @@
3139 4187 'mxchat_similarity_threshold',
3140 4188 array(
3141 4189 'type' => 'number',
3142 4190 'sanitize_callback' => function($value) {
3143 - $value = absint($value); // Ensure it's an integer
3144 - return min(max($value, 70), 85); // Enforce range
4191 + $value = absint($value);
4192 + return min(max($value, 20), 95);
3145 4193 },
3146 4194 'default' => 80,
3147 4195 )
3148 4196 );
@@ -3171,38 +4219,113 @@
3171 4219 'mxchat-chatbot',
3172 4220 'mxchat_chatbot_section'
3173 4221 );
3174 4222
3175 -
3176 - // Existing fields...
3177 4223 add_settings_field(
3178 - 'api_key',
3179 - esc_html__('OpenAI API Key', 'mxchat'),
3180 - array($this, 'api_key_callback'),
4224 + 'contextual_awareness_toggle',
4225 + esc_html__('Contextual Awareness', 'mxchat'),
4226 + array($this, 'mxchat_contextual_awareness_callback'),
3181 4227 'mxchat-chatbot',
3182 4228 'mxchat_chatbot_section'
3183 4229 );
3184 4230
4231 + add_settings_field(
4232 + 'api_key',
4233 + esc_html__('OpenAI API Key', 'mxchat'),
4234 + array($this, 'api_key_callback'),
4235 + 'mxchat-chatbot',
4236 + 'mxchat_chatbot_section',
4237 + array(
4238 + 'class' => 'mxchat-setting-row',
4239 + 'data-provider' => 'openai'
4240 + )
4241 + );
4242 +
4243 + add_settings_field(
4244 + 'xai_api_key',
4245 + esc_html__('X.AI API Key', 'mxchat'),
4246 + array($this, 'xai_api_key_callback'),
4247 + 'mxchat-chatbot',
4248 + 'mxchat_chatbot_section',
4249 + array(
4250 + 'class' => 'mxchat-setting-row',
4251 + 'data-provider' => 'xai'
4252 + )
4253 + );
4254 +
4255 + add_settings_field(
4256 + 'claude_api_key',
4257 + esc_html__('Claude API Key', 'mxchat'),
4258 + array($this, 'claude_api_key_callback'),
4259 + 'mxchat-chatbot',
4260 + 'mxchat_chatbot_section',
4261 + array(
4262 + 'class' => 'mxchat-setting-row',
4263 + 'data-provider' => 'claude'
4264 + )
4265 + );
4266 +
4267 + add_settings_field(
4268 + 'deepseek_api_key',
4269 + esc_html__('DeepSeek API Key', 'mxchat'),
4270 + array($this, 'deepseek_api_key_callback'),
4271 + 'mxchat-chatbot',
4272 + 'mxchat_chatbot_section',
4273 + array(
4274 + 'class' => 'mxchat-setting-row',
4275 + 'data-provider' => 'deepseek'
4276 + )
4277 + );
4278 +
4279 + add_settings_field(
4280 + 'gemini_api_key',
4281 + esc_html__('Google Gemini API Key', 'mxchat'),
4282 + array($this, 'gemini_api_key_callback'),
4283 + 'mxchat-chatbot',
4284 + 'mxchat_chatbot_section',
4285 + array(
4286 + 'class' => 'mxchat-setting-row',
4287 + 'data-provider' => 'gemini'
4288 + )
4289 + );
4290 +
4291 + add_settings_field(
4292 + 'voyage_api_key',
4293 + esc_html__('Voyage AI API Key', 'mxchat'),
4294 + array($this, 'voyage_api_key_callback'),
4295 + 'mxchat-chatbot',
4296 + 'mxchat_chatbot_section',
4297 + array(
4298 + 'class' => 'mxchat-setting-row',
4299 + 'data-provider' => 'voyage'
4300 + )
4301 + );
4302 +
3185 4303 add_settings_field(
3186 - 'xai_api_key',
3187 - esc_html__('X.AI API Key', 'mxchat'),
3188 - array($this, 'xai_api_key_callback'),
4304 + 'enable_streaming_toggle',
4305 + esc_html__('Enable Streaming', 'mxchat'),
4306 + array($this, 'enable_streaming_toggle_callback'),
3189 4307 'mxchat-chatbot',
3190 - 'mxchat_chatbot_section'
4308 + 'mxchat_chatbot_section', // Same section as your working toggle
4309 + array(
4310 + 'class' => 'mxchat-setting-row streaming-setting',
4311 + 'style' => 'display: none;' // Hidden by default, shown when OpenAI/Claude selected
4312 + )
3191 4313 );
3192 4314
4315 +
3193 4316 add_settings_field(
3194 - 'claude_api_key',
3195 - esc_html__('Claude API Key', 'mxchat'),
3196 - array($this, 'claude_api_key_callback'),
4317 + 'model',
4318 + esc_html__('Chat Model', 'mxchat'),
4319 + array($this, 'mxchat_model_callback'),
3197 4320 'mxchat-chatbot',
3198 4321 'mxchat_chatbot_section'
3199 4322 );
3200 4323
3201 4324 add_settings_field(
3202 - 'deepseek_api_key',
3203 - esc_html__('DeepSeek API Key', 'mxchat'),
3204 - array($this, 'deepseek_api_key_callback'),
4325 + 'embedding_model',
4326 + esc_html__('Embedding Model', 'mxchat'),
4327 + array($this, 'embedding_model_callback'),
3205 4328 'mxchat-chatbot',
3206 4329 'mxchat_chatbot_section'
3207 4330 );
3208 4331
@@ -3213,20 +4336,21 @@
3213 4336 'mxchat-chatbot',
3214 4337 'mxchat_chatbot_section'
3215 4338 );
3216 4339
4340 +
3217 4341 add_settings_field(
3218 - 'model',
3219 - esc_html__('Model', 'mxchat'),
3220 - array($this, 'mxchat_model_callback'),
4342 + 'top_bar_title',
4343 + esc_html__('Top Bar Title', 'mxchat'),
4344 + array($this, 'mxchat_top_bar_title_callback'),
3221 4345 'mxchat-chatbot',
3222 4346 'mxchat_chatbot_section'
3223 4347 );
3224 4348
3225 4349 add_settings_field(
3226 - 'top_bar_title',
3227 - esc_html__('Top Bar Title', 'mxchat'),
3228 - array($this, 'mxchat_top_bar_title_callback'),
4350 + 'ai_agent_text',
4351 + esc_html__('AI Agent Text', 'mxchat'),
4352 + array($this, 'mxchat_ai_agent_text_callback'),
3229 4353 'mxchat-chatbot',
3230 4354 'mxchat_chatbot_section'
3231 4355 );
3232 4356
@@ -3254,48 +4378,40 @@
3254 4378 'mxchat_chatbot_section'
3255 4379 );
3256 4380
3257 4381 add_settings_field(
3258 - 'intro_message',
3259 - esc_html__('Introductory Message', 'mxchat'),
3260 - array($this, 'mxchat_intro_message_callback'),
4382 + 'enable_name_field',
4383 + esc_html__('Require Name Field', 'mxchat'),
4384 + array($this, 'enable_name_field_callback'),
3261 4385 'mxchat-chatbot',
3262 4386 'mxchat_chatbot_section'
3263 4387 );
3264 4388
3265 4389 add_settings_field(
3266 - 'input_copy',
3267 - esc_html__('Input Copy', 'mxchat'),
3268 - array($this, 'mxchat_input_copy_callback'),
4390 + 'name_field_placeholder',
4391 + esc_html__('Name Field Placeholder', 'mxchat'),
4392 + array($this, 'name_field_placeholder_callback'),
3269 4393 'mxchat-chatbot',
3270 4394 'mxchat_chatbot_section'
3271 4395 );
3272 4396
3273 4397 add_settings_field(
3274 - 'rate_limit_logged_out',
3275 - __('Rate Limit for Logged-out Users', 'mxchat'),
3276 - array($this, 'mxchat_rate_limit_logged_out_callback'),
4398 + 'intro_message',
4399 + esc_html__('Introductory Message', 'mxchat'),
4400 + array($this, 'mxchat_intro_message_callback'),
3277 4401 'mxchat-chatbot',
3278 4402 'mxchat_chatbot_section'
3279 4403 );
3280 4404
3281 4405 add_settings_field(
3282 - 'rate_limit_roles',
3283 - __('Rate Limits by User Role', 'mxchat'),
3284 - array($this, 'mxchat_rate_limit_roles_callback'),
4406 + 'input_copy',
4407 + esc_html__('Input Copy', 'mxchat'),
4408 + array($this, 'mxchat_input_copy_callback'),
3285 4409 'mxchat-chatbot',
3286 4410 'mxchat_chatbot_section'
3287 4411 );
3288 4412
3289 4413 add_settings_field(
3290 - 'rate_limit_message',
3291 - esc_html__('Rate Limit Message', 'mxchat'),
3292 - array($this, 'mxchat_rate_limit_message_callback'),
3293 - 'mxchat-chatbot',
3294 - 'mxchat_chatbot_section'
3295 - );
3296 -
3297 - add_settings_field(
3298 4414 'pre_chat_message',
3299 4415 esc_html__('Chat Teaser Pop-up', 'mxchat'),
3300 4416 array($this, 'mxchat_pre_chat_message_callback'),
3301 4417 'mxchat-chatbot',
@@ -3335,9 +4451,9 @@
3335 4451 );
3336 4452
3337 4453 add_settings_field(
3338 4454 'popular_question_1',
3339 - esc_html__('Popular Question 1', 'mxchat'),
4455 + esc_html__('Quick Question 1', 'mxchat'),
3340 4456 array($this, 'mxchat_popular_question_1_callback'),
3341 4457 'mxchat-chatbot',
3342 4458 'mxchat_chatbot_section'
3343 4459 );
@@ -3343,9 +4459,9 @@
3343 4459 );
3344 4460
3345 4461 add_settings_field(
3346 4462 'popular_question_2',
3347 - esc_html__('Popular Question 2', 'mxchat'),
4463 + esc_html__('Quick Question 2', 'mxchat'),
3348 4464 array($this, 'mxchat_popular_question_2_callback'),
3349 4465 'mxchat-chatbot',
3350 4466 'mxchat_chatbot_section'
3351 4467 );
@@ -3351,9 +4467,9 @@
3351 4467 );
3352 4468
3353 4469 add_settings_field(
3354 4470 'popular_question_3',
3355 - esc_html__('Popular Question 3', 'mxchat'),
4471 + esc_html__('Quick Question 3', 'mxchat'),
3356 4472 array($this, 'mxchat_popular_question_3_callback'),
3357 4473 'mxchat-chatbot',
3358 4474 'mxchat_chatbot_section'
3359 4475 );
@@ -3359,9 +4475,9 @@
3359 4475 );
3360 4476
3361 4477 add_settings_field(
3362 4478 'additional_popular_questions',
3363 - esc_html__('Additional Popular Questions', 'mxchat'),
4479 + esc_html__('Additional Quick Questions', 'mxchat'),
3364 4480 array($this, 'mxchat_additional_popular_questions_callback'),
3365 4481 'mxchat-chatbot',
3366 4482 'mxchat_chatbot_section'
3367 4483 );
@@ -3366,8 +4482,16 @@
3366 4482 'mxchat_chatbot_section'
3367 4483 );
3368 4484
3369 4485
4486 + add_settings_field(
4487 + 'rate_limits',
4488 + __('Rate Limits Settings', 'mxchat'),
4489 + array($this, 'mxchat_rate_limits_callback'),
4490 + 'mxchat-chatbot',
4491 + 'mxchat_chatbot_section'
4492 + );
4493 +
3370 4494 // Loops Settings Section
3371 4495 add_settings_section(
3372 4496 'mxchat_loops_section',
3373 4497 esc_html__('Loops Settings', 'mxchat'),
@@ -3479,9 +4603,27 @@
3479 4603 'mxchat-embed',
3480 4604 'mxchat_pdf_intent_section'
3481 4605 );
3482 4606
4607 + // PDF Upload Button Toggle
3483 4608 add_settings_field(
4609 + 'show_pdf_upload_button',
4610 + __('Show PDF Upload Button', 'mxchat'),
4611 + array($this, 'mxchat_show_pdf_upload_button_callback'),
4612 + 'mxchat-embed',
4613 + 'mxchat_pdf_intent_section'
4614 + );
4615 +
4616 + // Word Upload Button Toggle
4617 + add_settings_field(
4618 + 'show_word_upload_button',
4619 + __('Show Word Upload Button', 'mxchat'),
4620 + array($this, 'mxchat_show_word_upload_button_callback'),
4621 + 'mxchat-embed',
4622 + 'mxchat_pdf_intent_section'
4623 + );
4624 +
4625 + add_settings_field(
3484 4626 'pdf_intent_trigger_text',
3485 4627 __('Intent Trigger Text', 'mxchat'),
3486 4628 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3487 4629 'mxchat-embed',
@@ -3546,8 +4688,16 @@
3546 4688 'mxchat_live_agent_section'
3547 4689 );
3548 4690
3549 4691 add_settings_field(
4692 + 'live_agent_user_ids',
4693 + __('Slack Agent User IDs', 'mxchat'),
4694 + array($this, 'mxchat_live_agent_user_ids_callback'),
4695 + 'mxchat-embed',
4696 + 'mxchat_live_agent_section'
4697 + );
4698 +
4699 + add_settings_field(
3550 4700 'live_agent_webhook_url',
3551 4701 __('Slack Webhook URL', 'mxchat'),
3552 4702 array($this, 'mxchat_live_agent_webhook_url_callback'),
3553 4703 'mxchat-embed',
@@ -3576,9 +4726,9 @@
3576 4726
3577 4727 // General Settings Section
3578 4728 add_settings_section(
3579 4729 'mxchat_general_section',
3580 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
4730 + esc_html__('YouTube Tutorials', 'mxchat'),
3581 4731 null,
3582 4732 'mxchat-general'
3583 4733 );
3584 4734 }
@@ -3583,9 +4733,8 @@
3583 4733 );
3584 4734 }
3585 4735
3586 4736 public function mxchat_prompts_page_init() {
3587 - // Register all settings as a single array
3588 4737 register_setting(
3589 4738 'mxchat_prompts_options',
3590 4739 'mxchat_prompts_options',
3591 4740 array(
@@ -3597,18 +4746,57 @@
3597 4746 'mxchat_use_pinecone' => 0,
3598 4747 'mxchat_pinecone_api_key' => '',
3599 4748 'mxchat_pinecone_environment' => '',
3600 4749 'mxchat_pinecone_index' => '',
3601 - 'mxchat_pinecone_host' => '', // Add this line
4750 + 'mxchat_pinecone_host' => '',
3602 4751 ),
3603 4752 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3604 4753 )
3605 4754 );
3606 4755
3607 - // Add settings saved message
3608 4756 add_action('admin_notices', array($this, 'sync_settings_notice'));
3609 4757 }
3610 4758
4759 +public function mxchat_transcripts_page_init() {
4760 + register_setting(
4761 + 'mxchat_transcripts_options',
4762 + 'mxchat_transcripts_options',
4763 + array(
4764 + 'type' => 'array',
4765 + 'description' => __('MXChat Transcripts Notification Settings', 'mxchat'),
4766 + 'default' => array(
4767 + 'mxchat_enable_notifications' => 0,
4768 + 'mxchat_notification_email' => get_option('admin_email'),
4769 + ),
4770 + 'sanitize_callback' => array($this, 'sanitize_transcripts_options'),
4771 + )
4772 + );
4773 +
4774 + add_settings_section(
4775 + 'mxchat_transcripts_notification_section',
4776 + esc_html__('Chat Notification Settings', 'mxchat'),
4777 + array($this, 'mxchat_transcripts_notification_section_callback'),
4778 + 'mxchat-transcripts'
4779 + );
4780 +
4781 + add_settings_field(
4782 + 'mxchat_enable_notifications',
4783 + esc_html__('Enable Chat Notifications', 'mxchat'),
4784 + array($this, 'mxchat_enable_notifications_callback'),
4785 + 'mxchat-transcripts',
4786 + 'mxchat_transcripts_notification_section'
4787 + );
4788 +
4789 + add_settings_field(
4790 + 'mxchat_notification_email',
4791 + esc_html__('Notification Email Address', 'mxchat'),
4792 + array($this, 'mxchat_notification_email_callback'),
4793 + 'mxchat-transcripts',
4794 + 'mxchat_transcripts_notification_section'
4795 + );
4796 +}
4797 +
4798 +
3611 4799 /**
3612 4800 * Sanitize all prompts options
3613 4801 *
3614 4802 * @param array $input The unsanitized options array
@@ -3667,9 +4855,8 @@
3667 4855
3668 4856 return $sanitized;
3669 4857 }
3670 4858
3671 -
3672 4859 public function sync_settings_notice() {
3673 4860 // Only show notice on our plugin page
3674 4861 if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3675 4862 return;
@@ -3690,139 +4877,129 @@
3690 4877 public function sanitize_sync_setting($input) {
3691 4878 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3692 4879 }
3693 4880
4881 +public function mxchat_rate_limits_callback() {
4882 + $all_options = get_option('mxchat_options', []);
3694 4883
4884 + // Define available rate limits
4885 + $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
3695 4886
3696 -public function mxchat_handle_activate_license() {
3697 - // Check nonce
3698 - if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3699 - wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
3700 - return;
3701 - }
4887 + // Define available timeframes
4888 + $timeframes = array(
4889 + 'hourly' => __('Per Hour', 'mxchat'),
4890 + 'daily' => __('Per Day', 'mxchat'),
4891 + 'weekly' => __('Per Week', 'mxchat'),
4892 + 'monthly' => __('Per Month', 'mxchat')
4893 + );
3702 4894
3703 - // Verify user capabilities
3704 - if (!current_user_can('manage_options')) {
3705 - wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
3706 - return;
3707 - }
4895 + // Get all roles plus a "logged_out" pseudo-role
4896 + $roles = wp_roles()->get_names();
4897 + $roles['logged_out'] = __('Logged Out Users', 'mxchat');
3708 4898
3709 - $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3710 - $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
4899 + // Start the wrapper
4900 + echo '<div class="pro-feature-wrapper active">';
4901 + echo '<div class="mxchat-rate-limits-container">';
3711 4902
3712 - if (empty($license_key) || empty($customer_email)) {
3713 - wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
3714 - return;
3715 - }
4903 + echo '<p class="description" style="margin-bottom: 20px;">' .
4904 + esc_html__('Set message limits for each user role and customize the experience when users reach those limits. You can use {limit}, {timeframe}, {count}, and {remaining} as placeholders.', 'mxchat') .
4905 + '</p>';
3716 4906
3717 - $product_id = 'MxChatPRO';
3718 - $response = wp_remote_get(
3719 - add_query_arg(
3720 - array(
3721 - 'wc-api' => 'software-api',
3722 - 'request' => 'activation',
3723 - 'email' => $customer_email,
3724 - 'license_key' => $license_key,
3725 - 'product_id' => $product_id
3726 - ),
3727 - 'http://mxchat.ai/'
3728 - )
3729 - );
4907 + // Add markdown link documentation
4908 + echo '<div class="notice notice-info inline" style="margin-bottom: 20px; padding: 10px;">';
4909 + echo '<p><strong>' . esc_html__('Markdown Links Supported:', 'mxchat') . '</strong></p>';
4910 + echo '<p>' . esc_html__('You can include clickable links in your custom messages using markdown syntax:', 'mxchat') . '</p>';
4911 + echo '<ul style="margin-left: 20px;">';
4912 + echo '<li><code>[Link text](https://example.com)</code> - Creates a clickable link</li>';
4913 + echo '<li><code>[Visit our pricing](https://example.com/pricing)</code> - Link with custom text</li>';
4914 + echo '<li><code>Plain URLs like https://example.com will also become clickable</code></li>';
4915 + echo '</ul>';
4916 + echo '</div>';
3730 4917
3731 - if (is_wp_error($response)) {
3732 - wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message());
3733 - return;
3734 - }
4918 + // Output the controls for each role
4919 + foreach ($roles as $role_id => $role_name) {
4920 + // Get saved options or defaults
4921 + $default_limit = ($role_id === 'logged_out') ? '10' : '100';
4922 + $default_timeframe = 'daily';
4923 + $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
3735 4924
3736 - $body = wp_remote_retrieve_body($response);
3737 - $data = json_decode($body);
4925 + $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
4926 + ? $all_options['rate_limits'][$role_id]['limit']
4927 + : $default_limit;
3738 4928
3739 - if ($data && isset($data->activated) && $data->activated) {
3740 - update_option('mxchat_license_status', 'active');
3741 - update_option('mxchat_pro_email', $customer_email);
3742 - update_option('mxchat_activation_key', $license_key);
3743 - wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
3744 - } else {
3745 - $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
3746 - update_option('mxchat_license_status', 'inactive');
3747 - update_option('mxchat_license_error', $error_message);
3748 - wp_send_json_error($error_message);
3749 - }
3750 -}
4929 + $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
4930 + ? $all_options['rate_limits'][$role_id]['timeframe']
4931 + : $default_timeframe;
3751 4932
4933 + $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
4934 + ? $all_options['rate_limits'][$role_id]['message']
4935 + : $default_message;
3752 4936
3753 -public function mxchat_rate_limit_logged_out_callback() {
3754 - // Load the entire 'mxchat_options' array
3755 - $all_options = get_option('mxchat_options', []);
4937 + // Output the row
4938 + echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
3756 4939
3757 - // Retrieve the saved rate limit or use the default value
3758 - $default_rate_limit = '10';
3759 - $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
4940 + // Role label
4941 + echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
3760 4942
3761 - // Define available rate limits
3762 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
4943 + // Controls section
4944 + echo '<div class="mxchat-rate-limit-controls-wrapper">';
3763 4945
3764 - // Output the dropdown
3765 - echo '<div class="pro-feature-wrapper active">';
3766 - echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3767 - foreach ($rate_limits as $limit) {
3768 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3769 - }
3770 - echo '</select>';
3771 - echo '<p class="description">' . esc_html__('Set the maximum number of messages a logged-out user can send within a 24-hour period.', 'mxchat') . '</p>';
3772 - echo '</div>';
3773 -}
4946 + // Rate limit and timeframe controls
4947 + echo '<div class="mxchat-rate-limit-controls">';
3774 4948
3775 -// Add this new callback function
3776 -public function mxchat_rate_limit_roles_callback() {
3777 - $all_options = get_option('mxchat_options', []);
3778 - $roles = wp_roles()->get_names();
3779 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
4949 + // Limit dropdown
4950 + echo '<div>';
4951 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
4952 + echo '<select
4953 + id="rate_limits_' . esc_attr($role_id) . '_limit"
4954 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
4955 + class="mxchat-autosave-field">';
4956 + foreach ($rate_limits as $limit) {
4957 + echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
4958 + }
4959 + echo '</select>';
4960 + echo '</div>';
3780 4961
3781 - echo '<div class="pro-feature-wrapper active mxchat-autosave-section">';
3782 -
3783 - foreach ($roles as $role_id => $role_name) {
3784 - $default_rate_limit = '100';
3785 - $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id])
3786 - ? $all_options['role_rate_limits'][$role_id]
3787 - : $default_rate_limit;
3788 -
3789 - echo '<div style="margin-bottom: 10px;">';
3790 - echo '<label style="display: inline-block; width: 150px;">' . esc_html($role_name) . ':</label>';
4962 + // Timeframe dropdown
4963 + echo '<div>';
4964 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
3791 4965 echo '<select
3792 - id="role_rate_limits_' . esc_attr($role_id) . '"
3793 - name="mxchat_options[role_rate_limits][' . esc_attr($role_id) . ']"
4966 + id="rate_limits_' . esc_attr($role_id) . '_timeframe"
4967 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
3794 4968 class="mxchat-autosave-field">';
3795 - foreach ($rate_limits as $limit) {
3796 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
4969 + foreach ($timeframes as $value => $label) {
4970 + echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
3797 4971 }
3798 4972 echo '</select>';
3799 4973 echo '</div>';
3800 - }
3801 4974
3802 - echo '<p class="description">' . esc_html__('Set the maximum number of messages users in each role can send within a 24-hour period.', 'mxchat') . '</p>';
3803 - echo '</div>';
3804 -}
4975 + echo '</div>'; // End controls
3805 4976
3806 -public function mxchat_rate_limit_message_callback() {
3807 - // Load the entire 'mxchat_options' array
3808 - $all_options = get_option('mxchat_options', []);
4977 + // Custom message textarea
4978 + echo '<div class="mxchat-rate-limit-message">';
4979 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
4980 + echo '<textarea
4981 + id="rate_limits_' . esc_attr($role_id) . '_message"
4982 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
4983 + class="mxchat-autosave-field"
4984 + placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
4985 + esc_textarea($custom_message) .
4986 + '</textarea>';
4987 + echo '<p class="description">' .
4988 + esc_html__('Example: Rate limit reached! [Visit our pricing page](https://example.com/pricing) to upgrade.', 'mxchat') .
4989 + '</p>';
4990 + echo '</div>'; // End message
3809 4991
3810 - // Retrieve the saved message or use the default value
3811 - $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
3812 - $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
4992 + echo '</div>'; // End controls wrapper
3813 4993
3814 - // Output the textarea
3815 - echo '<div class="pro-feature-wrapper active">';
3816 - printf(
3817 - '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
3818 - esc_textarea($rate_limit_message)
3819 - );
3820 - echo '<p class="description">' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '</p>';
3821 - echo '</div>';
4994 + echo '</div>'; // End row
4995 + }
4996 +
4997 + echo '</div>'; // End container
4998 +
4999 + echo '</div>'; // End pro-feature-wrapper
3822 5000 }
3823 5001
3824 -
3825 5002 private function mxchat_add_option_field($id, $title, $callback = '') {
3826 5003 add_settings_field(
3827 5004 $id,
3828 5005 __($title, 'mxchat'),
@@ -3832,94 +5009,89 @@
3832 5009 $id === 'model' ? ['label_for' => 'model'] : []
3833 5010 );
3834 5011 }
3835 5012
5013 +// OpenAI API Key
3836 5014 public function api_key_callback() {
3837 - // Retrieve from your stored 'api_key' in the mxchat_options array
3838 5015 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3839 5016
3840 - // Notice we changed the name to "api_key" (no array notation).
3841 - echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
5017 + echo '<div class="api-key-wrapper" data-provider="openai">';
5018 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
3842 5019 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3843 - echo '<p class="description">' . esc_html__('The OpenAI API key is required even when using other models as it is used for vector embedding functionality. You must add credits to your OpenAI billing account before use.', 'mxchat') . '</p>';
5020 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
5021 + echo '</div>';
3844 5022 }
5023 +
5024 +// X.AI API Key
3845 5025 public function xai_api_key_callback() {
3846 - // Check if the feature is activated (paid feature)
3847 - $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3848 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3849 -
3850 - // Retrieve the X.AI API key value
3851 5026 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3852 5027
3853 - // Render the input field for the X.AI API key
3854 - echo '<div class="' . esc_attr($class) . '">';
3855 - printf(
3856 - '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
3857 - $xaiApiKey,
3858 - $disabled
3859 - );
5028 + echo '<div class="api-key-wrapper" data-provider="xai">';
5029 + echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
3860 5030 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3861 -
3862 - // If the feature is not activated, show the overlay with a "Pro Only" message
3863 - if (!$this->is_activated) {
3864 - echo '<div class="pro-feature-overlay">';
3865 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
3866 - echo '</div>';
3867 - }
3868 -
5031 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3869 5032 echo '</div>';
3870 5033 }
5034 +// Claude API Key
3871 5035 public function claude_api_key_callback() {
3872 - // Check if the feature is activated (paid feature)
3873 - $disabled = $this->is_activated ? '' : 'disabled';
3874 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3875 -
3876 - // Retrieve the Claude API key value directly like the others
3877 5036 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3878 -
3879 - // Use direct name field like the other working API keys
3880 - echo '<div class="' . esc_attr($class) . '">';
3881 - printf(
3882 - '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
3883 - $claudeApiKey,
3884 - $disabled
3885 - );
5037 +
5038 + echo '<div class="api-key-wrapper" data-provider="claude">';
5039 + echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
3886 5040 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3887 -
3888 - if (!$this->is_activated) {
3889 - echo '<div class="pro-feature-overlay">';
3890 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
3891 - echo '</div>';
3892 - }
5041 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3893 5042 echo '</div>';
3894 5043 }
5044 +
5045 +// DeepSeek API Key
3895 5046 public function deepseek_api_key_callback() {
3896 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
3897 5047 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
3898 5048
3899 - // Notice we changed the name to "deepseek_api_key" (no array notation).
3900 - echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" />';
5049 + echo '<div class="api-key-wrapper" data-provider="deepseek">';
5050 + echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
3901 5051 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5052 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
5053 + echo '</div>';
3902 5054 }
3903 5055
5056 +// Gemini API Key
5057 +public function gemini_api_key_callback() {
5058 + $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
3904 5059
5060 + echo '<div class="api-key-wrapper" data-provider="gemini">';
5061 + echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
5062 + echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5063 + echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
5064 + echo '</div>';
5065 +}
3905 5066
5067 +// Voyage API Key
5068 +public function voyage_api_key_callback() {
5069 + $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
3906 5070
5071 + echo '<div class="api-key-wrapper" data-provider="voyage">';
5072 + echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5073 + echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5074 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
5075 + echo '</div>';
5076 +}
3907 5077
3908 5078 public function mxchat_loops_api_key_callback() {
3909 - // Support both old and new format
3910 5079 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
3911 5080
3912 - echo '<div class="api-key-wrapper">';
5081 + // Hidden fields to "trap" autofill
5082 + echo '<input type="text" style="display:none" autocomplete="username" />';
5083 + echo '<input type="password" style="display:none" autocomplete="current-password" />';
5084 +
5085 + echo '<div class="api-key-wrapper" data-provider="loops">';
3913 5086 echo sprintf(
3914 - '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
5087 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
3915 5088 $loops_api_key
3916 5089 );
3917 5090 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3918 5091 echo '</div>';
3919 - echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5092 + echo '<p class="description">' . esc_html__('Enter your Loops API Key here. Once entered, refreshed page to load list (See FAQ for details)', 'mxchat') . '</p>';
3920 5093 }
3921 -
3922 5094 public function mxchat_loops_mailing_list_callback() {
3923 5095 // Add error handling and type checking
3924 5096 $loops_api_key = '';
3925 5097 $selected_list = '';
@@ -3937,8 +5109,12 @@
3937 5109 if (!empty($loops_api_key)) {
3938 5110 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
3939 5111 if (is_array($lists) && !empty($lists)) {
3940 5112 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
5113 +
5114 + // Add a default "Select a list" option
5115 + echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
5116 +
3941 5117 foreach ($lists as $list) {
3942 5118 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
3943 5119 echo sprintf(
3944 5120 '<option value="%s" %s>%s</option>',
@@ -3948,8 +5124,9 @@
3948 5124 );
3949 5125 }
3950 5126 }
3951 5127 echo '</select>';
5128 + echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
3952 5129 } else {
3953 5130 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
3954 5131 }
3955 5132 } else {
@@ -3960,14 +5137,13 @@
3960 5137 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
3961 5138 $triggered_response = isset($this->options['triggered_phrase_response'])
3962 5139 ? $this->options['triggered_phrase_response']
3963 5140 : $default_response;
3964 -
3965 5141 echo sprintf(
3966 5142 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
3967 5143 esc_textarea($triggered_response)
3968 5144 );
3969 - echo '<p class="description">' . esc_html__('Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.', 'mxchat') . '</p>';
5145 + 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>';
3970 5146 }
3971 5147
3972 5148 public function mxchat_email_capture_response_callback() {
3973 5149 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
@@ -3973,16 +5149,14 @@
3973 5149 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
3974 5150 $email_capture_response = isset($this->options['email_capture_response'])
3975 5151 ? $this->options['email_capture_response']
3976 5152 : $default_response;
3977 -
3978 5153 echo sprintf(
3979 5154 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
3980 5155 esc_textarea($email_capture_response)
3981 5156 );
3982 - echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
5157 + 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>';
3983 5158 }
3984 -
3985 5159 public function mxchat_pre_chat_message_callback() {
3986 5160 // Load the entire 'mxchat_options' array
3987 5161 $all_options = get_option('mxchat_options', []);
3988 5162
@@ -3997,49 +5171,137 @@
3997 5171 );
3998 5172 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>';
3999 5173 }
4000 5174
4001 -
4002 -
4003 5175 // Callback for AI Instructions textarea
4004 5176 public function system_prompt_instructions_callback() {
4005 5177 // Retrieve the current value of the system prompt instructions
4006 5178 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4007 -
4008 5179 // Render the textarea field
4009 5180 printf(
4010 5181 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4011 5182 $instructions
4012 5183 );
5184 + // Provide a helpful description with sample instructions button
5185 + 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>';
5186 + echo '<div class="mxchat-instructions-container">';
5187 + echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
5188 + 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">';
5189 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5190 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5191 + echo '</svg>';
5192 + echo esc_html__('View Sample Instructions', 'mxchat');
5193 + echo '</button>';
5194 + echo '</div>';
4013 5195
4014 - // Provide a helpful description
4015 - 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>';
5196 + // Add modal to WordPress admin footer instead of inline
5197 + add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
4016 5198 }
4017 5199
5200 +// New method to render modal in admin footer
5201 +public function render_sample_instructions_modal() {
5202 + static $modal_rendered = false;
5203 + if ($modal_rendered) return; // Prevent duplicate modals
5204 + $modal_rendered = true;
4018 5205
5206 + echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
5207 + echo '<div class="mxchat-instructions-modal-content">';
5208 + echo '<div class="mxchat-instructions-modal-header">';
5209 + echo '<h3 class="mxchat-instructions-modal-title">';
5210 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5211 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5212 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5213 + echo '</svg>';
5214 + echo esc_html__('Sample AI Instructions', 'mxchat');
5215 + echo '</h3>';
5216 + echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
5217 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5218 + echo '<line x1="18" y1="6" x2="6" y2="18"/>';
5219 + echo '<line x1="6" y1="6" x2="18" y2="18"/>';
5220 + echo '</svg>';
5221 + echo '</button>';
5222 + echo '</div>';
5223 + echo '<div class="mxchat-instructions-modal-body">';
5224 + echo '<div class="mxchat-instructions-content">';
5225 + echo esc_html('You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
5226 +
5227 +# Response Style - CRITICALLY IMPORTANT
5228 +- MAXIMUM LENGTH: 1-3 short sentences per response
5229 +- Ultra-concise: Get straight to the answer with no filler
5230 +- No introductions like "Sure!" or "I\'d be happy to help"
5231 +- No phrases like "based on my knowledge" or "according to information"
5232 +- No explanatory text before giving the answer
5233 +- No summaries or repetition
5234 +- Hyperlink all URLs
5235 +- Respond in user\'s language
5236 +- Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
5237 +
5238 +# Knowledge Base Requirements - PREVENT HALLUCINATIONS
5239 +- ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
5240 +- If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
5241 +- NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
5242 +- When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
5243 +- Better to admit insufficient information than provide inaccurate answers');
5244 + echo '</div>';
5245 + echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
5246 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
5247 + echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
5248 + echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
5249 + echo '</svg>';
5250 + echo esc_html__('Copy Instructions', 'mxchat');
5251 + echo '</button>';
5252 + echo '</div>';
5253 + echo '<div class="mxchat-instructions-modal-footer">';
5254 + echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
5255 + echo '</div>';
5256 + echo '</div>';
5257 + echo '</div>';
5258 +}
5259 +
5260 +
4019 5261 public function mxchat_model_callback() {
4020 5262 // Define available models grouped by provider
4021 5263 $models = array(
5264 + esc_html__('Google Gemini Models', 'mxchat') => array(
5265 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5266 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5267 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5268 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5269 + ),
4022 5270 esc_html__('X.AI Models', 'mxchat') => array(
4023 - 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4024 - 'grok-2' => esc_html__('Grok 2', 'mxchat')
5271 + 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
5272 + 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
5273 + 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
5274 + 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
5275 + 'grok-2' => esc_html__('Grok 2', 'mxchat'),
5276 + 'grok-4-0709' => esc_html__('Grok 4 (Latest Flagship)', 'mxchat'), // Added new Grok-4 model
4025 5277 ),
4026 5278 esc_html__('DeepSeek Models', 'mxchat') => array(
4027 5279 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4028 5280 ),
4029 5281 esc_html__('Claude Models', 'mxchat') => array(
4030 - 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
4031 - 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
5282 + 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
5283 + 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
5284 + 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
5285 + 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Intelligent)', 'mxchat'),
5286 + 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
4032 5287 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4033 - 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
5288 + 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat'),
4034 5289 ),
4035 5290 esc_html__('OpenAI Models', 'mxchat') => array(
5291 + // NEW: GPT-5 family
5292 + 'gpt-5' => esc_html__('GPT-5 (Flagship for Coding, Reasoning & Agents)', 'mxchat'),
5293 + 'gpt-5-mini' => esc_html__('GPT-5 Mini (Faster, Cost-Efficient for Precise Prompts)', 'mxchat'),
5294 + 'gpt-5-nano' => esc_html__('GPT-5 Nano (Fastest & Cheapest for Summarization/Classification)', 'mxchat'),
5295 +
5296 + // Existing OpenAI models
5297 + 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4036 5298 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4037 5299 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4038 5300 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4039 5301 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4040 - 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4041 - )
5302 + 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat'),
5303 + ),
4042 5304 );
4043 5305
4044 5306 // Retrieve the currently selected model from saved options
4045 5307 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
@@ -4044,9 +5306,9 @@
4044 5306 // Retrieve the currently selected model from saved options
4045 5307 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4046 5308
4047 5309 // Begin the select dropdown
4048 - echo '<select id="model" name="model">'; // No array notation in name attribute
5310 + echo '<select id="model" name="model">';
4049 5311
4050 5312 // Iterate over groups of models
4051 5313 foreach ($models as $group_label => $group_models) {
4052 5314 echo '<optgroup label="' . esc_attr($group_label) . '">';
@@ -4051,14 +5313,10 @@
4051 5313 foreach ($models as $group_label => $group_models) {
4052 5314 echo '<optgroup label="' . esc_attr($group_label) . '">';
4053 5315
4054 5316 foreach ($group_models as $model_value => $model_label) {
4055 - // Disable Pro-only models for non-activated users
4056 - $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4057 - $label_suffix = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? esc_html__(' (Pro Only)', 'mxchat') : '';
4058 -
4059 - // Output the option element
4060 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
5317 + // All models enabled - no disabled attribute or Pro Only label
5318 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4061 5319 }
4062 5320
4063 5321 echo '</optgroup>';
4064 5322 }
@@ -4065,14 +5323,70 @@
4065 5323
4066 5324 // Close the select dropdown
4067 5325 echo '</select>';
4068 5326
4069 - // Add a description below the dropdown
4070 - echo '<p class="description">' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
5327 + // Updated description to remove mention of Pro-only models
5328 + echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
4071 5329 }
4072 5330
4073 5331
5332 +// Update your existing callback method
5333 +public function enable_streaming_toggle_callback() {
5334 + // Get value from options array, default to 'on'
5335 + $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
5336 + $checked = ($enabled === 'on') ? 'checked' : '';
4074 5337
5338 + echo '<label class="toggle-switch">';
5339 + echo sprintf(
5340 + '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
5341 + esc_attr($checked)
5342 + );
5343 + echo '<span class="slider"></span>';
5344 + echo '</label>';
5345 + echo '<p class="description">' .
5346 + esc_html__('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, and Grok). When disabled, responses will load all at once.', 'mxchat') .
5347 + '</p>';
5348 +
5349 + // Test button with better styling
5350 + echo '<div style="margin-top: 15px;">';
5351 + echo '<button type="button" id="mxchat-test-streaming-btn" class="button button-secondary">';
5352 + echo '<span class="dashicons dashicons-admin-tools" style="vertical-align: middle; margin-right: 5px;"></span>';
5353 + echo esc_html__('Test Streaming Compatibility', 'mxchat');
5354 + echo '</button>';
5355 + echo '<p id="mxchat-test-streaming-result" style="margin-top:10px; font-weight: bold;"></p>';
5356 + echo '</div>';
5357 +}
5358 +
5359 +
5360 +// Callback function for embedding model selection
5361 +public function embedding_model_callback() {
5362 + $models = array(
5363 + esc_html__('OpenAI Embeddings', 'mxchat') => array(
5364 + 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
5365 + 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
5366 + 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
5367 + ),
5368 + esc_html__('Voyage AI Embeddings', 'mxchat') => array(
5369 + 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
5370 + ),
5371 + esc_html__('Google Gemini Embeddings', 'mxchat') => array(
5372 + 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
5373 + )
5374 + );
5375 + $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
5376 + echo '<select id="embedding_model" name="embedding_model">';
5377 + foreach ($models as $group_label => $group_models) {
5378 + echo '<optgroup label="' . esc_attr($group_label) . '">';
5379 + foreach ($group_models as $model_value => $model_label) {
5380 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
5381 + }
5382 + echo '</optgroup>';
5383 + }
5384 + echo '</select>';
5385 + echo '<p class="description"><span class="red-warning">IMPORTANT:</span> Select the model for vector embeddings. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
5386 +}
5387 +
5388 +
4075 5389 public function mxchat_top_bar_title_callback() {
4076 5390 // Retrieve the current value of the top bar title from saved options
4077 5391 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4078 5392
@@ -4081,9 +5395,18 @@
4081 5395
4082 5396 // Add a description
4083 5397 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4084 5398 }
5399 +public function mxchat_ai_agent_text_callback() {
5400 + // Retrieve the current value of the AI agent text from saved options
5401 + $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
5402 + // Render the input field
5403 + echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
5404 + // Add a description
5405 + echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
5406 +}
4085 5407
5408 +
4086 5409 public function enable_email_block_callback() {
4087 5410 // Load full plugin options array
4088 5411 $all_options = get_option('mxchat_options', []);
4089 5412
@@ -4102,10 +5425,8 @@
4102 5425 echo '</label>';
4103 5426 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>';
4104 5427 }
4105 5428
4106 -
4107 -
4108 5429 public function email_blocker_header_content_callback() {
4109 5430 // Load the entire 'mxchat_options' array
4110 5431 $all_options = get_option('mxchat_options', []);
4111 5432
@@ -4113,14 +5434,15 @@
4113 5434 $content = isset($all_options['email_blocker_header_content'])
4114 5435 ? $all_options['email_blocker_header_content']
4115 5436 : '';
4116 5437
4117 - // Render the textarea
5438 + // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4118 5439 echo '<textarea
4119 5440 id="email_blocker_header_content"
4120 5441 name="email_blocker_header_content"
4121 5442 rows="5"
4122 5443 cols="70"
5444 + data-setting="email_blocker_header_content"
4123 5445 >' . esc_textarea($content) . '</textarea>';
4124 5446
4125 5447 echo '<p class="description">';
4126 5448 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');
@@ -4126,9 +5448,8 @@
4126 5448 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');
4127 5449 echo '</p>';
4128 5450 }
4129 5451
4130 -
4131 5452 public function email_blocker_button_text_callback() {
4132 5453 // Load the entire 'mxchat_options' array
4133 5454 $all_options = get_option('mxchat_options', []);
4134 5455
@@ -4144,28 +5465,55 @@
4144 5465 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4145 5466 echo '</p>';
4146 5467 }
4147 5468
5469 +//Enable name field callback
5470 +public function enable_name_field_callback() {
5471 + // Load full plugin options array
5472 + $all_options = get_option('mxchat_options', []);
5473 + // Get the value, default to 'off'
5474 + $enable_name_field = isset($all_options['enable_name_field']) ? $all_options['enable_name_field'] : 'off';
5475 + // Check if it's 'on'
5476 + $checked = ($enable_name_field === 'on') ? 'checked' : '';
5477 + echo '<label class="toggle-switch">';
5478 + echo sprintf(
5479 + '<input type="checkbox" id="enable_name_field" name="enable_name_field" value="on" %s />',
5480 + esc_attr($checked)
5481 + );
5482 + echo '<span class="slider"></span>';
5483 + echo '</label>';
5484 + echo '<p class="description">' . esc_html__('Enable this to also require users to enter their name along with their email before chatting.', 'mxchat') . '</p>';
5485 +}
5486 +//Name field placeholder callback
5487 +public function name_field_placeholder_callback() {
5488 + $all_options = get_option('mxchat_options', []);
5489 + $placeholder = isset($all_options['name_field_placeholder'])
5490 + ? $all_options['name_field_placeholder']
5491 + : esc_html__('Enter your name', 'mxchat');
4148 5492
5493 + echo '<input type="text" id="name_field_placeholder" name="name_field_placeholder" value="' . esc_attr($placeholder) . '" style="width: 300px;" />';
5494 + echo '<p class="description">';
5495 + echo esc_html__('Placeholder text for the name field.', 'mxchat');
5496 + echo '</p>';
5497 +}
4149 5498
5499 +
5500 +
4150 5501 public function mxchat_intro_message_callback() {
4151 5502 // Load the entire 'mxchat_options' array
4152 5503 $all_options = get_option('mxchat_options', []);
4153 -
4154 5504 // Retrieve the saved intro message or use the default
4155 5505 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4156 5506 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4157 -
4158 - // Output the textarea with the saved value
5507 + // Output the textarea with the saved value without escaping HTML
4159 5508 ?>
4160 - <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
5509 + <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4161 5510 <p class="description">
4162 - <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
5511 + <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4163 5512 </p>
4164 5513 <?php
4165 5514 }
4166 5515
4167 -
4168 5516 public function mxchat_input_copy_callback() {
4169 5517 // Load the entire 'mxchat_options' array
4170 5518 $all_options = get_option('mxchat_options', []);
4171 5519
@@ -4631,8 +5979,23 @@
4631 5979 '</p>';
4632 5980
4633 5981 }
4634 5982
5983 +public function mxchat_contextual_awareness_callback() {
5984 + // Get value from options array, default to 'off'
5985 + $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
5986 + $checked = ($contextual_awareness === 'on') ? 'checked' : '';
5987 + echo '<label class="toggle-switch">';
5988 + echo sprintf(
5989 + '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
5990 + esc_attr($checked)
5991 + );
5992 + echo '<span class="slider"></span>';
5993 + echo '</label>';
5994 + echo '<p class="description">' .
5995 + 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') .
5996 + '</p>';
5997 +}
4635 5998
4636 5999
4637 6000 public function mxchat_privacy_toggle_callback() {
4638 6001 // Load from mxchat_options array
@@ -4735,13 +6098,13 @@
4735 6098 // Render the input field
4736 6099 printf(
4737 6100 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4738 6101 esc_attr($popular_question_1),
4739 - esc_attr__('Enter Popular Question 1', 'mxchat')
6102 + esc_attr__('Enter Quick Question 1', 'mxchat')
4740 6103 );
4741 6104
4742 6105 // Add a description for the field
4743 - echo '<p class="description">' . esc_html__('This will be the first popular question in the chatbot.', 'mxchat') . '</p>';
6106 + echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
4744 6107 }
4745 6108
4746 6109
4747 6110 public function mxchat_popular_question_2_callback() {
@@ -4754,13 +6117,13 @@
4754 6117 // Render the input field
4755 6118 printf(
4756 6119 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4757 6120 esc_attr($popular_question_2),
4758 - esc_attr__('Enter Popular Question 2', 'mxchat')
6121 + esc_attr__('Enter Quick Question 2', 'mxchat')
4759 6122 );
4760 6123
4761 6124 // Add a description for the field
4762 - echo '<p class="description">' . esc_html__('This will be the second popular question in the chatbot.', 'mxchat') . '</p>';
6125 + echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
4763 6126 }
4764 6127
4765 6128
4766 6129 public function mxchat_popular_question_3_callback() {
@@ -4773,13 +6136,13 @@
4773 6136 // Render the input field
4774 6137 printf(
4775 6138 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
4776 6139 esc_attr($popular_question_3),
4777 - esc_attr(__('Enter Popular Question 3', 'mxchat'))
6140 + esc_attr(__('Enter Quick Question 3', 'mxchat'))
4778 6141 );
4779 6142
4780 6143 // Add a description for the field
4781 - echo '<p class="description">' . esc_html__('This will be the third popular question in the chatbot.', 'mxchat') . '</p>';
6144 + echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
4782 6145 }
4783 6146
4784 6147 public function mxchat_additional_popular_questions_callback() {
4785 6148 $options = get_option('mxchat_options', []);
@@ -4800,9 +6163,9 @@
4800 6163 <button type="button" class="button mxchat-remove-question"
4801 6164 aria-label="%s">%s</button>
4802 6165 </div>',
4803 6166 esc_attr($question),
4804 - esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
6167 + esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
4805 6168 $index,
4806 6169 esc_attr(__('Remove question', 'mxchat')),
4807 6170 esc_html__('Remove', 'mxchat')
4808 6171 );
@@ -4817,9 +6180,9 @@
4817 6180 data-question-index="0" />
4818 6181 <button type="button" class="button mxchat-remove-question"
4819 6182 aria-label="%s">%s</button>
4820 6183 </div>',
4821 - esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
6184 + esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
4822 6185 esc_attr(__('Remove question', 'mxchat')),
4823 6186 esc_html__('Remove', 'mxchat')
4824 6187 );
4825 6188 }
@@ -4828,9 +6191,9 @@
4828 6191 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
4829 6192 esc_attr(__('Add question', 'mxchat')),
4830 6193 esc_html__('Add Question', 'mxchat')
4831 6194 );
4832 - echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
6195 + echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
4833 6196 echo '</div>';
4834 6197 }
4835 6198
4836 6199 public function mxchat_brave_api_key_callback() {
@@ -4929,141 +6292,116 @@
4929 6292 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
4930 6293 }
4931 6294
4932 6295 public function mxchat_chat_toolbar_toggle_callback() {
4933 - // Get chat toolbar toggle value with fallback
4934 - $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
4935 - $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
6296 + // Get chat toolbar toggle value with fallback
6297 + $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
6298 + $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
4936 6299
4937 - // Check if the plugin is activated (paid feature)
4938 - $disabled = $this->is_activated ? '' : 'disabled';
4939 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6300 + // Output the toggle switch
6301 + echo '<label class="toggle-switch">';
6302 + echo sprintf(
6303 + '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
6304 + esc_attr($checked)
6305 + );
6306 + echo '<span class="slider"></span>';
6307 + echo '</label>';
4940 6308
4941 - echo '<div class="' . esc_attr($class) . '">';
6309 + echo '<p class="description">' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '</p>';
6310 +}
6311 +/**
6312 + * Callback for PDF upload button toggle setting
6313 + */
6314 +public function mxchat_show_pdf_upload_button_callback() {
6315 + // Get toggle value with fallback
6316 + $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
6317 + $checked = ($show_pdf_button === 'on') ? 'checked' : '';
4942 6318
4943 - // Output the toggle switch
4944 - echo '<label class="toggle-switch">';
4945 - echo sprintf(
4946 - '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
4947 - esc_attr($checked),
4948 - esc_attr($disabled)
4949 - );
4950 - echo '<span class="slider"></span>';
4951 - echo '</label>';
6319 + // Output the toggle switch
6320 + echo '<label class="toggle-switch">';
6321 + echo sprintf(
6322 + '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
6323 + esc_attr($checked)
6324 + );
6325 + echo '<span class="slider"></span>';
6326 + echo '</label>';
4952 6327
4953 - // Pro feature overlay
4954 - if (!$this->is_activated) {
4955 - echo '<div class="pro-feature-overlay">';
4956 - echo '<a href="https://mxchat.ai/" target="_blank">';
4957 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4958 - echo '</a>';
4959 - echo '</div>';
4960 - }
6328 + echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
6329 +}
6330 +/**
6331 + * Callback for Word upload button toggle setting
6332 + */
6333 +public function mxchat_show_word_upload_button_callback() {
6334 + // Get toggle value with fallback
6335 + $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
6336 + $checked = ($show_word_button === 'on') ? 'checked' : '';
4961 6337
4962 - echo '</div>';
4963 - echo '<p class="description">' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '</p>';
6338 + // Output the toggle switch
6339 + echo '<label class="toggle-switch">';
6340 + echo sprintf(
6341 + '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
6342 + esc_attr($checked)
6343 + );
6344 + echo '<span class="slider"></span>';
6345 + echo '</label>';
6346 +
6347 + echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
4964 6348 }
4965 6349
4966 -
4967 6350 public function mxchat_pdf_intent_trigger_text_callback() {
4968 - $disabled = $this->is_activated ? '' : 'disabled';
4969 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4970 6351 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
4971 6352
4972 - echo '<div class="' . esc_attr($class) . '">';
4973 6353 echo sprintf(
4974 6354 '<textarea id="pdf_intent_trigger_text"
4975 6355 name="pdf_intent_trigger_text"
4976 6356 rows="3"
4977 6357 cols="50"
4978 - placeholder="%s"
4979 - %s>%s</textarea>',
6358 + placeholder="%s">%s</textarea>',
4980 6359 esc_attr__('Enter trigger text', 'mxchat'),
4981 - esc_attr($disabled),
4982 6360 isset($this->options['pdf_intent_trigger_text'])
4983 6361 ? esc_textarea($this->options['pdf_intent_trigger_text'])
4984 6362 : esc_textarea($default_text)
4985 6363 );
4986 6364 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
4987 -
4988 - if (!$this->is_activated) {
4989 - echo '<div class="pro-feature-overlay">';
4990 - echo '<a href="https://mxchat.ai/" target="_blank">';
4991 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4992 - echo '</a>';
4993 - echo '</div>';
4994 - }
4995 - echo '</div>';
4996 6365 }
4997 6366
4998 6367 public function mxchat_pdf_intent_success_text_callback() {
4999 - $disabled = $this->is_activated ? '' : 'disabled';
5000 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5001 6368 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5002 6369
5003 - echo '<div class="' . esc_attr($class) . '">';
5004 6370 echo sprintf(
5005 6371 '<textarea id="pdf_intent_success_text"
5006 6372 name="pdf_intent_success_text"
5007 6373 rows="3"
5008 6374 cols="50"
5009 - placeholder="%s"
5010 - %s>%s</textarea>',
6375 + placeholder="%s">%s</textarea>',
5011 6376 esc_attr__('Enter success text', 'mxchat'),
5012 - esc_attr($disabled),
5013 6377 isset($this->options['pdf_intent_success_text'])
5014 6378 ? esc_textarea($this->options['pdf_intent_success_text'])
5015 6379 : esc_textarea($default_text)
5016 6380 );
5017 6381 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5018 -
5019 - if (!$this->is_activated) {
5020 - echo '<div class="pro-feature-overlay">';
5021 - echo '<a href="https://mxchat.ai/" target="_blank">';
5022 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5023 - echo '</a>';
5024 - echo '</div>';
5025 - }
5026 - echo '</div>';
5027 6382 }
5028 6383
5029 6384 public function mxchat_pdf_intent_error_text_callback() {
5030 - $disabled = $this->is_activated ? '' : 'disabled';
5031 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5032 6385 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5033 6386
5034 - echo '<div class="' . esc_attr($class) . '">';
5035 6387 echo sprintf(
5036 6388 '<textarea id="pdf_intent_error_text"
5037 6389 name="pdf_intent_error_text"
5038 6390 rows="3"
5039 6391 cols="50"
5040 - placeholder="%s"
5041 - %s>%s</textarea>',
6392 + placeholder="%s">%s</textarea>',
5042 6393 esc_attr__('Enter error text', 'mxchat'),
5043 - esc_attr($disabled),
5044 6394 isset($this->options['pdf_intent_error_text'])
5045 6395 ? esc_textarea($this->options['pdf_intent_error_text'])
5046 6396 : esc_textarea($default_text)
5047 6397 );
5048 6398 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5049 -
5050 - if (!$this->is_activated) {
5051 - echo '<div class="pro-feature-overlay">';
5052 - echo '<a href="https://mxchat.ai/" target="_blank">';
5053 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5054 - echo '</a>';
5055 - echo '</div>';
5056 - }
5057 - echo '</div>';
5058 6399 }
5059 6400
5060 6401 public function mxchat_pdf_max_pages_callback() {
5061 - $disabled = $this->is_activated ? '' : 'disabled';
5062 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5063 6402 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5064 6403
5065 - echo '<div class="' . esc_attr($class) . '">';
5066 6404 echo sprintf(
5067 6405 '<input type="range"
5068 6406 id="pdf_max_pages"
5069 6407 name="pdf_max_pages"
@@ -5069,37 +6407,24 @@
5069 6407 name="pdf_max_pages"
5070 6408 min="1"
5071 6409 max="69"
5072 6410 value="%d"
5073 - %s
5074 6411 class="range-slider" />',
5075 - esc_attr($max_pages),
5076 - esc_attr($disabled)
6412 + esc_attr($max_pages)
5077 6413 );
5078 6414 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5079 6415 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5080 -
5081 - if (!$this->is_activated) {
5082 - echo '<div class="pro-feature-overlay">';
5083 - echo '<a href="https://mxchat.ai/" target="_blank">';
5084 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5085 - echo '</a>';
5086 - echo '</div>';
5087 - }
5088 - echo '</div>';
5089 6416 }
5090 6417
5091 6418 public function mxchat_live_agent_status_callback() {
5092 - $disabled = $this->is_activated ? '' : 'disabled';
5093 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5094 - $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off';
6419 + // Always get fresh options instead of using cached $this->options
6420 + $fresh_options = get_option('mxchat_options');
6421 + $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
5095 6422
5096 - echo '<div class="' . esc_attr($class) . '">';
5097 6423 echo '<label class="toggle-switch">';
5098 6424 echo sprintf(
5099 - '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s %s />',
5100 - checked($status, 'on', false),
5101 - esc_attr($disabled)
6425 + '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
6426 + checked($status, 'on', false)
5102 6427 );
5103 6428 echo '<span class="slider"></span>';
5104 6429 echo '</label>';
5105 6430 echo '<label for="live_agent_status" class="mxchat-status-label">';
@@ -5104,98 +6429,83 @@
5104 6429 echo '</label>';
5105 6430 echo '<label for="live_agent_status" class="mxchat-status-label">';
5106 6431 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5107 6432 echo '</label>';
5108 -
5109 - if (!$this->is_activated) {
5110 - echo '<div class="pro-feature-overlay">';
5111 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5112 - echo '</div>';
5113 - }
5114 - echo '</div>';
5115 6433 }
5116 6434
5117 -
5118 -// Away Message Callback
5119 6435 public function mxchat_live_agent_away_message_callback() {
5120 - $disabled = $this->is_activated ? '' : 'disabled';
5121 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5122 6436 $message = isset($this->options['live_agent_away_message'])
5123 6437 ? $this->options['live_agent_away_message']
5124 6438 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5125 6439
5126 - echo '<div class="' . esc_attr($class) . '">';
5127 6440 printf(
5128 - '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50" %s>%s</textarea>',
5129 - esc_attr($disabled),
6441 + '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
5130 6442 esc_textarea($message)
5131 6443 );
5132 6444 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5133 -
5134 - if (!$this->is_activated) {
5135 - echo '<div class="pro-feature-overlay">';
5136 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5137 - echo '</div>';
5138 - }
5139 - echo '</div>';
5140 6445 }
5141 6446
5142 6447 public function mxchat_live_agent_notification_message_callback() {
5143 - $disabled = $this->is_activated ? '' : 'disabled';
5144 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5145 6448 $message = isset($this->options['live_agent_notification_message'])
5146 6449 ? $this->options['live_agent_notification_message']
5147 6450 : __('Live agent has been notified.', 'mxchat');
5148 6451
5149 - echo '<div class="' . esc_attr($class) . '">';
5150 6452 printf(
5151 - '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50" %s>%s</textarea>',
5152 - esc_attr($disabled),
6453 + '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
5153 6454 esc_textarea($message)
5154 6455 );
5155 6456 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5156 -
5157 - if (!$this->is_activated) {
5158 - echo '<div class="pro-feature-overlay">';
5159 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5160 - echo '</div>';
5161 - }
5162 - echo '</div>';
5163 6457 }
5164 6458
5165 6459 public function mxchat_live_agent_webhook_url_callback() {
5166 - $disabled = $this->is_activated ? '' : 'disabled';
5167 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5168 6460 $webhook_url = isset($this->options['live_agent_webhook_url'])
5169 6461 ? esc_url($this->options['live_agent_webhook_url'])
5170 6462 : esc_url(get_option('live_agent_webhook_url', ''));
5171 6463
5172 - echo '<div class="' . esc_attr($class) . '">';
5173 6464 printf(
5174 - '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" %s />',
5175 - $webhook_url,
5176 - esc_attr($disabled)
6465 + '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
6466 + $webhook_url
5177 6467 );
5178 6468 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5179 6469 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
6470 +}
5180 6471
5181 - if (!$this->is_activated) {
5182 - echo '<div class="pro-feature-overlay">';
5183 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5184 - echo '</div>';
5185 - }
5186 - echo '</div>';
6472 +public function mxchat_live_agent_secret_key_callback() {
6473 + printf(
6474 + '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
6475 + isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
6476 + );
6477 + echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6478 + echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5187 6479 }
5188 6480
6481 +public function mxchat_live_agent_bot_token_callback() {
6482 + printf(
6483 + '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
6484 + isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
6485 + );
6486 + echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6487 + echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
6488 +}
5189 6489
6490 +public function mxchat_live_agent_user_ids_callback() {
6491 + $user_ids = isset($this->options['live_agent_user_ids'])
6492 + ? esc_textarea($this->options['live_agent_user_ids'])
6493 + : '';
6494 +
6495 + printf(
6496 + '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
6497 + $user_ids
6498 + );
6499 + 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>';
6500 +}
6501 +
5190 6502 public function mxchat_similarity_threshold_callback() {
5191 6503 // Load from mxchat_options array
5192 6504 $options = get_option('mxchat_options', []);
5193 6505
5194 - // Get value with backwards compatibility
5195 - $threshold = isset($options['similarity_threshold'])
5196 - ? $options['similarity_threshold']
5197 - : get_option('mxchat_similarity_threshold', 80);
6506 + // Get value from options array with default of 80
6507 + $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
5198 6508
5199 6509 echo '<div class="slider-container">';
5200 6510 echo sprintf(
5201 6511 '<input type="range"
@@ -5200,9 +6510,9 @@
5200 6510 echo sprintf(
5201 6511 '<input type="range"
5202 6512 id="similarity_threshold"
5203 6513 name="similarity_threshold"
5204 - min="70"
6514 + min="20"
5205 6515 max="85"
5206 6516 step="1"
5207 6517 value="%s"
5208 6518 class="range-slider" />',
@@ -5212,186 +6522,195 @@
5212 6522 '<span id="threshold_value" class="range-value">%s</span>',
5213 6523 esc_html($threshold)
5214 6524 );
5215 6525 echo '</div>';
5216 -
5217 6526 echo '<p class="description">';
5218 - echo esc_html__('Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge.', 'mxchat');
6527 + 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');
5219 6528 echo '</p>';
5220 6529 }
5221 6530
6531 +public function mxchat_enqueue_admin_assets() {
6532 + // Get plugin version (define this in your main plugin file)
6533 + $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '2.4.6';
5222 6534
6535 + // Use file modification time for development (remove in production)
6536 + if (defined('WP_DEBUG') && WP_DEBUG) {
6537 + $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
6538 + }
5223 6539
5224 -public function mxchat_live_agent_secret_key_callback() {
5225 - $disabled = $this->is_activated ? '' : 'disabled';
5226 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6540 + $current_page = isset($_GET['page']) ? $_GET['page'] : '';
6541 + $plugin_url = plugin_dir_url(__FILE__) . '../';
5227 6542
5228 - echo '<div class="' . esc_attr($class) . '">';
5229 - printf(
5230 - '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" %s />',
5231 - isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
5232 - esc_attr($disabled)
5233 - );
5234 - echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5235 - echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
6543 + // Only load on MxChat pages
6544 + if (strpos($current_page, 'mxchat') === false) {
6545 + return;
6546 + }
5236 6547
5237 - if (!$this->is_activated) {
5238 - echo '<div class="pro-feature-overlay">';
5239 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5240 - echo '</div>';
5241 - }
5242 - echo '</div>';
6548 + // Always load these on all MxChat pages
6549 + $this->enqueue_core_admin_assets($plugin_url, $version);
6550 + $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
6551 + $this->localize_admin_scripts($current_page);
5243 6552 }
6553 +private function enqueue_core_admin_assets($plugin_url, $version) {
6554 + // Core admin styles
6555 + wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
6556 + wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
5244 6557
5245 -public function mxchat_live_agent_bot_token_callback() {
5246 - $disabled = $this->is_activated ? '' : 'disabled';
5247 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5248 -
5249 - echo '<div class="' . esc_attr($class) . '">';
5250 - printf(
5251 - '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" %s />',
5252 - isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
5253 - esc_attr($disabled)
5254 - );
5255 - echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5256 - echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5257 -
5258 - if (!$this->is_activated) {
5259 - echo '<div class="pro-feature-overlay">';
5260 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5261 - echo '</div>';
5262 - }
5263 - echo '</div>';
6558 + // Core admin scripts
6559 + wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
5264 6560 }
6561 +private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
6562 + switch ($current_page) {
6563 + case 'mxchat-prompts':
6564 + // Knowledge processing page assets
6565 + wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
6566 + wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
6567 + // NEW: Add the knowledge processing script
6568 + wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
6569 + break;
5265 6570
5266 -public function mxchat_enqueue_admin_assets() {
5267 - wp_enqueue_style('wp-color-picker');
6571 + case 'mxchat-transcripts':
6572 + wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
6573 + wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
6574 + break;
5268 6575
5269 - // Get the plugin version or file modification time for cache busting
5270 - $plugin_version = '2.0.4'; // Replace this with your plugin's version
6576 + case 'mxchat-actions':
6577 + wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
6578 + break;
5271 6579
5272 - // File paths
5273 - $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5274 - $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5275 - $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5276 - $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5277 - $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5278 - $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
6580 + case 'mxchat-activation':
6581 + wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
6582 + break;
6583 + default:
6584 + wp_enqueue_script(
6585 + 'mxchat-test-streaming-js',
6586 + $plugin_url . 'js/mxchat-test-streaming.js',
6587 + ['jquery'],
6588 + $version,
6589 + true
6590 + );
5279 6591
5280 - // Check if files exist and get modification times
5281 - $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5282 - $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5283 - $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5284 - $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5285 - $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5286 - $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5287 -
5288 - // Enqueue scripts and styles with corrected paths
5289 - wp_enqueue_script(
5290 - 'mxchat-color-picker',
5291 - plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
5292 - array('wp-color-picker'),
5293 - $color_picker_version,
5294 - true
5295 - );
5296 -
5297 - wp_enqueue_script(
5298 - 'mxchat-embedding-check',
5299 - plugin_dir_url(__FILE__) . '../js/embedding-check.js',
5300 - array(),
5301 - $embedding_check_version,
5302 - true
5303 - );
5304 -
5305 - wp_enqueue_script(
5306 - 'mxchat-transcripts-js',
5307 - plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5308 - array('jquery'),
5309 - $transcripts_js_version,
5310 - true
5311 - );
5312 -
5313 - wp_enqueue_script(
5314 - 'mxchat-admin-js',
5315 - plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5316 - array('jquery'),
5317 - $plugin_version,
5318 - true
5319 - );
5320 -
5321 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
6592 + wp_localize_script('mxchat-test-streaming-js', 'mxchatTestStreamingAjax', [
6593 + 'ajax_url' => admin_url('admin-ajax.php'),
6594 + 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce')
6595 + ]);
6596 + break;
6597 + }
6598 +}
6599 +private function localize_admin_scripts($current_page) {
6600 + // Base localization data for main admin script
6601 + $base_data = array(
5322 6602 'ajax_url' => admin_url('admin-ajax.php'),
6603 + 'nonce' => wp_create_nonce('mxchat_status_nonce'),
6604 + 'admin_url' => admin_url(),
5323 6605 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5324 6606 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5325 6607 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5326 - ));
6608 + 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
6609 + 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6610 + 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
6611 + 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
6612 + 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6613 + 'is_activated' => $this->is_activated ? '1' : '0',
6614 + 'status_refresh_interval' => 5000,
6615 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
6616 + 'ajaxurl' => admin_url('admin-ajax.php') // For jQuery fallback
6617 + );
5327 6618
5328 - // Enqueue the admin CSS
5329 - wp_enqueue_style(
5330 - 'mxchat-admin-css',
5331 - plugin_dir_url(__FILE__) . '../css/admin-style.css',
5332 - array(),
5333 - $admin_css_version
5334 - );
6619 + // Localize main admin script with base data
6620 + wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
5335 6621
5336 - wp_enqueue_style(
5337 - 'mxchat-knowledge-css',
5338 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5339 - array(),
5340 - $knowledge_css_version
5341 - );
6622 + // Page-specific localizations
6623 + $this->localize_page_specific_scripts($current_page);
6624 +}
6625 +private function localize_page_specific_scripts($current_page) {
6626 + switch ($current_page) {
6627 + case 'mxchat-prompts':
6628 + // Status updater localization
6629 + wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
6630 + 'ajax_url' => admin_url('admin-ajax.php'),
6631 + 'nonce' => wp_create_nonce('mxchat_status_nonce')
6632 + ));
5342 6633
6634 + // Content selector localization
6635 + wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
6636 + 'ajaxurl' => admin_url('admin-ajax.php'),
6637 + 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
6638 + 'i18n' => array(
6639 + 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
6640 + 'selectAll' => __('Select All', 'mxchat'),
6641 + 'process' => __('Process Selected', 'mxchat'),
6642 + 'cancel' => __('Cancel', 'mxchat'),
6643 + 'noResults' => __('No content found.', 'mxchat')
6644 + )
6645 + ));
5343 6646
5344 - wp_enqueue_style(
5345 - 'mxchat-intent-css',
5346 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5347 - array(),
5348 - $intent_css_version
5349 - );
6647 + // NEW: Knowledge processing script localization using mxchatAdmin
6648 + wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
6649 + 'ajax_url' => admin_url('admin-ajax.php'),
6650 + 'status_nonce' => wp_create_nonce('mxchat_status_nonce'), // Note: changed 'nonce' to 'status_nonce'
6651 + 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
6652 + 'admin_url' => admin_url(),
6653 + 'ajaxurl' => admin_url('admin-ajax.php'),
6654 + 'status_refresh_interval' => 2000
6655 + ));
6656 + break;
5350 6657
5351 - // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5352 - wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5353 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
5354 - 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5355 - ) );
6658 + case 'mxchat-activation':
6659 + wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
6660 + 'ajax_url' => admin_url('admin-ajax.php'),
6661 + 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
6662 + ));
6663 + break;
5356 6664
5357 - // Localize the script for color picker and settings
5358 - wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6665 + case 'mxchat-settings':
6666 + default:
6667 + // Color picker and settings localization
6668 + wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6669 + 'ajax_url' => admin_url('admin-ajax.php'),
6670 + 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
6671 + 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
6672 + 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
6673 + 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
6674 + 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
6675 + 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
6676 + 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
6677 + 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
6678 + 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
6679 + 'icon_color' => $this->options['icon_color'] ?? '#fff',
6680 + 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
6681 + 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
6682 + 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
6683 + 'loops_api_key' => $this->options['loops_api_key'] ?? '',
6684 + 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
6685 + 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
6686 + 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
6687 + 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
6688 + 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
6689 + 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
6690 + 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
6691 + 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
6692 + 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
6693 + 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
6694 + 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
6695 + 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
6696 + 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
6697 + 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
6698 + 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
6699 + 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
6700 + 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
6701 + 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6702 + ));
6703 + break;
6704 + }
6705 +
6706 + // Additional localization that was in the original code
6707 + wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
5359 6708 'ajax_url' => admin_url('admin-ajax.php'),
5360 - 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5361 - 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5362 - 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5363 - 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5364 - 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5365 - 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5366 - 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5367 - 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5368 - 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5369 - 'icon_color' => $this->options['icon_color'] ?? '#fff',
5370 - 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5371 - 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5372 - 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5373 - 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5374 - 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5375 - 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5376 - 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5377 - 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
5378 - 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
5379 - 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
5380 - 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5381 - 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5382 - 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5383 - 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5384 - 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5385 - 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5386 - 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5387 - 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5388 - 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5389 - 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6709 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5390 6710 ));
5391 6711 }
5392 6712
5393 -
5394 6713 public function mxchat_sanitize($input) {
5395 6714 $new_input = array();
5396 6715
5397 6716 if (isset($input['api_key'])) {
@@ -5399,9 +6718,9 @@
5399 6718 }
5400 6719
5401 6720 if (isset($input['similarity_threshold'])) {
5402 6721 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5403 - $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
6722 + $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5404 6723 }
5405 6724
5406 6725 if (isset($input['xai_api_key'])) {
5407 6726 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
@@ -5410,12 +6729,24 @@
5410 6729 if (isset($input['claude_api_key'])) {
5411 6730 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5412 6731 }
5413 6732
6733 + if (isset($input['enable_streaming_toggle'])) {
6734 + $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
6735 + } else {
6736 + // If checkbox not checked, it won't be in $input, so set to 'off'
6737 + $new_input['enable_streaming_toggle'] = 'off';
6738 + }
6739 +
6740 +
5414 6741 if (isset($input['deepseek_api_key'])) {
5415 6742 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5416 6743 }
5417 6744
6745 + if (isset($input['gemini_api_key'])) {
6746 + $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
6747 + }
6748 +
5418 6749 if (isset($input['enable_woocommerce_integration'])) {
5419 6750 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5420 6751 }
5421 6752
@@ -5448,12 +6779,20 @@
5448 6779 if (isset($input['append_to_body'])) {
5449 6780 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5450 6781 }
5451 6782
6783 + if (isset($input['contextual_awareness_toggle'])) {
6784 + $new_input['contextual_awareness_toggle'] = $input['contextual_awareness_toggle'] === 'on' ? 'on' : 'off';
6785 +}
6786 +
5452 6787 if (isset($input['top_bar_title'])) {
5453 6788 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5454 6789 }
5455 6790
6791 + if (isset($input['ai_agent_text'])) {
6792 + $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
6793 + }
6794 +
5456 6795 if (isset($input['enable_email_block'])) {
5457 6796 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5458 6797 }
5459 6798
@@ -5463,11 +6802,20 @@
5463 6802 }
5464 6803 if (isset($input['email_blocker_button_text'])) {
5465 6804 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5466 6805 }
5467 -
6806 + // NEW: Sanitize name field toggle
6807 + if (isset($input['enable_name_field'])) {
6808 + $new_input['enable_name_field'] = ($input['enable_name_field'] === 'on') ? 'on' : 'off';
6809 + } else {
6810 + $new_input['enable_name_field'] = 'off';
6811 + }
6812 + // NEW: Sanitize name field placeholder
6813 + if (isset($input['name_field_placeholder'])) {
6814 + $new_input['name_field_placeholder'] = sanitize_text_field($input['name_field_placeholder']);
6815 + }
5468 6816 if (isset($input['intro_message'])) {
5469 - $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
6817 + $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5470 6818 }
5471 6819
5472 6820 if (isset($input['input_copy'])) {
5473 6821 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
@@ -5476,58 +6824,108 @@
5476 6824 if (isset($input['rate_limit_message'])) {
5477 6825 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5478 6826 }
5479 6827
5480 - if (isset($input['rate_limit_logged_out'])) {
5481 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5482 - $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
6828 +// Handle the new rate limits format
6829 +if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
6830 + $new_input['rate_limits'] = array();
6831 + $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
6832 + $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
5483 6833
5484 - if (in_array($rate_limit, $allowed_limits, true)) {
5485 - $new_input['rate_limit_logged_out'] = $rate_limit;
5486 - } else {
5487 - $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
6834 + foreach ($input['rate_limits'] as $role_id => $settings) {
6835 + $new_input['rate_limits'][$role_id] = array();
6836 +
6837 + // Sanitize limit
6838 + if (isset($settings['limit'])) {
6839 + $limit = sanitize_text_field($settings['limit']);
6840 + if (in_array($limit, $allowed_limits, true)) {
6841 + $new_input['rate_limits'][$role_id]['limit'] = $limit;
6842 + } else {
6843 + $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
6844 + }
5488 6845 }
5489 - }
5490 6846
5491 - // Handle role rate limits
5492 - if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) {
5493 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5494 - $new_input['role_rate_limits'] = array();
5495 -
5496 - foreach ($input['role_rate_limits'] as $role_id => $rate_limit) {
5497 - $rate_limit = sanitize_text_field($rate_limit);
5498 - if (in_array($rate_limit, $allowed_limits, true)) {
5499 - $new_input['role_rate_limits'][$role_id] = $rate_limit;
6847 + // Sanitize timeframe
6848 + if (isset($settings['timeframe'])) {
6849 + $timeframe = sanitize_text_field($settings['timeframe']);
6850 + if (in_array($timeframe, $allowed_timeframes, true)) {
6851 + $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
5500 6852 } else {
5501 - $new_input['role_rate_limits'][$role_id] = '100'; // Default
6853 + $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
5502 6854 }
5503 6855 }
6856 +
6857 + // Sanitize message
6858 + if (isset($settings['message'])) {
6859 + $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
6860 + }
5504 6861 }
6862 +}
5505 6863
5506 6864 if (isset($input['pre_chat_message'])) {
5507 6865 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5508 6866 }
5509 6867
5510 - if (isset($input['model'])) {
6868 + if (isset($input['voyage_api_key'])) {
6869 + $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
6870 + }
6871 +
6872 + // Add to your sanitize function
6873 + if (isset($input['embedding_model'])) {
5511 6874 $allowed_models = array(
5512 - 'grok-beta',
5513 - 'grok-2',
5514 - 'deepseek-chat',
5515 - 'claude-3-5-sonnet-20241022',
5516 - 'claude-3-opus-20240229',
5517 - 'claude-3-sonnet-20240229',
5518 - 'claude-3-haiku-20240307',
5519 - 'gpt-4o',
5520 - 'gpt-4o-mini',
5521 - 'gpt-4-turbo',
5522 - 'gpt-4',
5523 - 'gpt-3.5-turbo',
6875 + 'text-embedding-ada-002',
6876 + 'text-embedding-3-small',
6877 + 'text-embedding-3-large',
6878 + 'voyage-3-large',
6879 + 'gemini-embedding-exp-03-07'
5524 6880 );
5525 - if (in_array($input['model'], $allowed_models)) {
5526 - $new_input['model'] = sanitize_text_field($input['model']);
6881 + if (in_array($input['embedding_model'], $allowed_models)) {
6882 + $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5527 6883 }
5528 6884 }
5529 6885
6886 +if (isset($input['model'])) {
6887 + $allowed_models = array(
6888 + 'gemini-2.0-flash',
6889 + 'gemini-2.0-flash-lite',
6890 + 'gemini-1.5-pro',
6891 + 'gemini-1.5-flash',
6892 +
6893 + 'grok-4-0709', // New model: Grok 4 added here
6894 + 'grok-3-beta',
6895 + 'grok-3-fast-beta',
6896 + 'grok-3-mini-beta',
6897 + 'grok-3-mini-fast-beta',
6898 + 'grok-2',
6899 +
6900 + 'deepseek-chat',
6901 +
6902 + 'claude-opus-4-20250514',
6903 + 'claude-sonnet-4-20250514',
6904 + 'claude-3-7-sonnet-20250219',
6905 + 'claude-3-5-sonnet-20241022',
6906 + 'claude-3-opus-20240229',
6907 + 'claude-3-sonnet-20240229',
6908 + 'claude-3-haiku-20240307',
6909 +
6910 + // NEW: GPT-5 family
6911 + 'gpt-5',
6912 + 'gpt-5-mini',
6913 + 'gpt-5-nano',
6914 +
6915 + 'gpt-4o',
6916 + 'gpt-4.1-2025-04-14',
6917 + 'gpt-4o-mini',
6918 + 'gpt-4-turbo',
6919 + 'gpt-4',
6920 + 'gpt-3.5-turbo',
6921 + );
6922 + if (in_array($input['model'], $allowed_models)) {
6923 + $new_input['model'] = sanitize_text_field($input['model']);
6924 + }
6925 +}
6926 +
6927 +
5530 6928 if (isset($input['close_button_color'])) {
5531 6929 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5532 6930 }
5533 6931
@@ -5582,8 +6980,12 @@
5582 6980 if (isset($input['top_bar_bg_color'])) {
5583 6981 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5584 6982 }
5585 6983
6984 + if (isset($input['quick_questions_toggle_color'])) {
6985 + $new_input['quick_questions_toggle_color'] = sanitize_hex_color($input['quick_questions_toggle_color']);
6986 + }
6987 +
5586 6988 if (isset($input['send_button_font_color'])) {
5587 6989 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5588 6990 }
5589 6991
@@ -5641,15 +7043,14 @@
5641 7043 if (isset($input['loops_mailing_list'])) {
5642 7044 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5643 7045 }
5644 7046
5645 - // Sanitize Triggered Phrase Response
7047 +// Sanitize Triggered Phrase Response
5646 7048 if (isset($input['triggered_phrase_response'])) {
5647 7049 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5648 7050 }
5649 -
5650 7051 if (isset($input['email_capture_response'])) {
5651 - $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
7052 + $new_input['email_capture_response'] = wp_kses_post($input['email_capture_response']);
5652 7053 }
5653 7054
5654 7055 // Sanitize Brave Search Settings
5655 7056 if (isset($input['brave_api_key'])) {
@@ -5682,8 +7083,22 @@
5682 7083 if (isset($input['chat_toolbar_toggle'])) {
5683 7084 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5684 7085 }
5685 7086
7087 + // Sanitize PDF upload button toggle
7088 + if (isset($input['show_pdf_upload_button'])) {
7089 + $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
7090 + } else {
7091 + $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
7092 + }
7093 +
7094 + // Sanitize Word upload button toggle
7095 + if (isset($input['show_word_upload_button'])) {
7096 + $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
7097 + } else {
7098 + $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
7099 + }
7100 +
5686 7101 if (isset($input['pdf_intent_trigger_text'])) {
5687 7102 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5688 7103 }
5689 7104
@@ -5713,8 +7128,12 @@
5713 7128 if (isset($input['live_agent_bot_token'])) {
5714 7129 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
5715 7130 }
5716 7131
7132 + if (isset($input['live_agent_user_ids'])) {
7133 + $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
7134 + }
7135 +
5717 7136 if (isset($input['live_agent_status'])) {
5718 7137 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
5719 7138 }
5720 7139 if (isset($input['live_agent_away_message'])) {
@@ -5737,62 +7156,10 @@
5737 7156 }
5738 7157
5739 7158
5740 7159
5741 -private static function mxchat_extract_main_content($html) {
5742 - if (empty($html)) {
5743 - //error_log('mxchat_extract_main_content: Empty HTML content received');
5744 - return '';
5745 - }
5746 7160
5747 - try {
5748 - $dom = new DOMDocument;
5749 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
5750 7161
5751 - // Simple load of HTML, with @ to suppress warnings
5752 - @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
5753 -
5754 - $xpath = new DOMXPath($dom);
5755 -
5756 - // Simplified selectors focusing on common content areas
5757 - $selectors = [
5758 - '//article',
5759 - '//*[@id="content"]',
5760 - '//*[@class="entry-content"]',
5761 - '//main'
5762 - ];
5763 -
5764 - foreach ($selectors as $selector) {
5765 - $nodes = $xpath->query($selector);
5766 - if ($nodes && $nodes->length > 0) {
5767 - $content = '';
5768 - foreach ($nodes as $node) {
5769 - $content .= $dom->saveHTML($node);
5770 - }
5771 - if (!empty($content)) {
5772 - return $content;
5773 - }
5774 - }
5775 - }
5776 -
5777 - // Fallback: Return the entire body content if no specific selector matches
5778 - $body = $dom->getElementsByTagName('body');
5779 - if ($body->length > 0) {
5780 - return $dom->saveHTML($body->item(0));
5781 - }
5782 -
5783 - // Last resort: return the original HTML
5784 - return $html;
5785 -
5786 - } catch (Exception $e) {
5787 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
5788 - return $html; // Return original HTML if parsing fails
5789 - } finally {
5790 - libxml_clear_errors();
5791 - }
5792 -}
5793 -
5794 -
5795 7162 private function mxchat_fetch_loops_mailing_lists($api_key) {
5796 7163 $url = 'https://app.loops.so/api/v1/lists';
5797 7164 $response = wp_remote_get($url, array(
5798 7165 'headers' => array(
@@ -5810,12 +7177,8 @@
5810 7177
5811 7178 return isset($lists) && is_array($lists) ? $lists : array();
5812 7179 }
5813 7180
5814 -
5815 -
5816 -
5817 -
5818 7181 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
5819 7182 if (empty($vec1) || empty($vec2)) {
5820 7183 return 0.0;
5821 7184 }
@@ -5836,8 +7199,441 @@
5836 7199 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
5837 7200 }
5838 7201 }
5839 7202
7203 +/**
7204 + * Validates nonce and deletes all chat prompts
7205 + */
7206 +public function mxchat_handle_delete_all_prompts() {
7207 + error_log('=== DELETE ALL DEBUG START ===');
7208 +
7209 + // Verify nonce
7210 + if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
7211 + error_log('DEBUG: Nonce verification failed');
7212 + wp_die(__('Nonce verification failed.', 'mxchat'));
7213 + }
7214 +
7215 + // Check permissions
7216 + if (!current_user_can('manage_options')) {
7217 + error_log('DEBUG: Permission check failed');
7218 + wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
7219 + }
7220 +
7221 + // Get bot_id from POST data
7222 + $bot_id = isset($_POST['bot_id']) ? sanitize_text_field($_POST['bot_id']) : 'default';
7223 + error_log('DEBUG: Bot ID from POST: ' . $bot_id);
7224 +
7225 + $success = true;
7226 + $error_messages = array();
7227 +
7228 + // Get bot-specific Pinecone configuration
7229 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
7230 + $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($bot_id);
7231 +
7232 + error_log('DEBUG: Pinecone options retrieved:');
7233 + error_log(' - Use Pinecone: ' . ($pinecone_options['mxchat_use_pinecone'] ?? 'NOT SET'));
7234 + error_log(' - API Key: ' . (empty($pinecone_options['mxchat_pinecone_api_key']) ? 'EMPTY' : 'SET'));
7235 + error_log(' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
7236 +
7237 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
7238 +
7239 + if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
7240 + error_log('[MXCHAT-DELETE] Pinecone is enabled for bot ' . $bot_id . ' - deleting all from Pinecone');
7241 +
7242 + // Delete from bot-specific Pinecone index
7243 + $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
7244 +
7245 + error_log('DEBUG: Delete all result: ' . ($result['success'] ? 'SUCCESS' : 'FAILED'));
7246 + if (!$result['success']) {
7247 + error_log('DEBUG: Delete all error: ' . $result['message']);
7248 + }
7249 +
7250 + if (!$result['success']) {
7251 + $success = false;
7252 + $error_messages[] = $result['message'];
7253 + }
7254 +
7255 + // No cache clearing needed since we removed caching
7256 +
7257 + } else {
7258 + error_log('[MXCHAT-DELETE] Pinecone not enabled for bot ' . $bot_id . ' - deleting from WordPress database');
7259 +
7260 + // Delete from WordPress database
7261 + global $wpdb;
7262 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
7263 +
7264 + // If multi-bot is active and we have a specific bot, delete only that bot's content
7265 + if ($bot_id !== 'default' && class_exists('MxChat_Multi_Bot_Manager')) {
7266 + $result = $wpdb->delete(
7267 + $table_name,
7268 + array('bot_id' => $bot_id),
7269 + array('%s')
7270 + );
7271 + } else {
7272 + // Delete all content for default bot
7273 + $result = $wpdb->query("DELETE FROM {$table_name}");
7274 + }
7275 +
7276 + if ($result === false) {
7277 + $success = false;
7278 + $error_messages[] = 'Failed to delete from WordPress database';
7279 + }
7280 + }
7281 +
7282 + // No cache clearing needed since we removed caching
7283 +
7284 + error_log('=== DELETE ALL DEBUG END ===');
7285 +
7286 + // Redirect back with a success message and bot_id
7287 + $redirect_url = add_query_arg(array(
7288 + 'page' => 'mxchat-prompts',
7289 + 'bot_id' => $bot_id,
7290 + 'all_deleted' => $success ? 'true' : 'false'
7291 + ), admin_url('admin.php'));
7292 +
7293 + wp_safe_redirect($redirect_url);
7294 + exit;
7295 +}
7296 +
7297 +
7298 +
7299 + /**
7300 + * Handles deletion prompt with nonce validation
7301 + */
7302 + public function mxchat_handle_delete_prompt() {
7303 + // Sanitize and validate nonce
7304 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
7305 + if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
7306 + wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
7307 + }
7308 +
7309 + // Check permissions
7310 + if (!current_user_can('manage_options')) {
7311 + wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
7312 + }
7313 +
7314 + // Get ID and source parameters
7315 + $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
7316 + $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
7317 +
7318 + if (empty($id)) {
7319 + wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
7320 + }
7321 +
7322 + $success = false;
7323 + $error_message = '';
7324 +
7325 + // Check if Pinecone is enabled and determine source automatically if not specified
7326 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
7327 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
7328 +
7329 + // If source is not specified, determine based on Pinecone configuration
7330 + if (empty($source)) {
7331 + $source = $use_pinecone ? 'pinecone' : 'wordpress';
7332 + }
7333 +
7334 + if ($source === 'pinecone' || $use_pinecone) {
7335 + //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
7336 +
7337 + // Handle Pinecone deletion
7338 + if (empty($pinecone_options['mxchat_pinecone_host']) ||
7339 + empty($pinecone_options['mxchat_pinecone_api_key'])) {
7340 + wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
7341 + }
7342 +
7343 + // Delete from Pinecone using the vector ID directly
7344 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
7345 + $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
7346 + $id,
7347 + $pinecone_options['mxchat_pinecone_api_key'],
7348 + $pinecone_options['mxchat_pinecone_host']
7349 + );
7350 + if ($result['success']) {
7351 + $success = true;
7352 +
7353 + // Remove from vector cache
7354 + $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
7355 + $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
7356 +
7357 + set_transient('mxchat_admin_notice_success',
7358 + esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
7359 + } else {
7360 + $error_message = $result['message'];
7361 + set_transient('mxchat_admin_notice_error',
7362 + esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
7363 + }
7364 + } else {
7365 + //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
7366 +
7367 + // Handle WordPress database deletion
7368 + global $wpdb;
7369 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
7370 +
7371 + // Clear cache and delete prompt
7372 + wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
7373 +
7374 + $result = $wpdb->delete(
7375 + $table_name,
7376 + array('id' => intval($id)),
7377 + array('%d')
7378 + );
7379 +
7380 + if ($result !== false) {
7381 + $success = true;
7382 + set_transient('mxchat_admin_notice_success',
7383 + esc_html__('Entry deleted successfully.', 'mxchat'), 30);
7384 + } else {
7385 + set_transient('mxchat_admin_notice_error',
7386 + esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
7387 + }
7388 + }
7389 +
7390 + // Redirect back to the prompts page
7391 + wp_safe_redirect(add_query_arg(
7392 + array(
7393 + 'page' => 'mxchat-prompts',
7394 + 'deleted' => $success ? 'true' : 'false'
7395 + ),
7396 + admin_url('admin.php')
7397 + ));
7398 + exit;
7399 + }
7400 +
7401 +
7402 +
7403 +
7404 + /**
7405 + * Averages multiple vectors into a single vector
7406 + */
7407 + private function mxchat_average_vectors($vectors) {
7408 + $vector_length = count($vectors[0]);
7409 + $sum_vector = array_fill(0, $vector_length, 0);
7410 +
7411 + foreach ($vectors as $vector) {
7412 + for ($i = 0; $i < $vector_length; $i++) {
7413 + $sum_vector[$i] += $vector[$i];
7414 + }
7415 + }
7416 +
7417 + // Divide each component by the number of vectors to get the average
7418 + $num_vectors = count($vectors);
7419 + for ($i = 0; $i < $vector_length; $i++) {
7420 + $sum_vector[$i] /= $num_vectors;
7421 + }
7422 +
7423 + return $sum_vector;
7424 + }
7425 +
7426 +
7427 +
7428 +
7429 + /**
7430 + * Generates embeddings from input text for MXChat
7431 + */
7432 + public function mxchat_generate_embedding($text) {
7433 + // Enable detailed logging for debugging
7434 + //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
7435 + //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
7436 +
7437 + $options = get_option('mxchat_options');
7438 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
7439 + //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
7440 +
7441 + // Determine provider and endpoint
7442 + if (strpos($selected_model, 'voyage') === 0) {
7443 + $api_key = $options['voyage_api_key'] ?? '';
7444 + $endpoint = 'https://api.voyageai.com/v1/embeddings';
7445 + $provider_name = 'Voyage AI';
7446 + //error_log('[MXCHAT-EMBED] Using Voyage AI API');
7447 + } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
7448 + $api_key = $options['gemini_api_key'] ?? '';
7449 + $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
7450 + $provider_name = 'Google Gemini';
7451 + //error_log('[MXCHAT-EMBED] Using Google Gemini API');
7452 + } else {
7453 + $api_key = $options['api_key'] ?? '';
7454 + $endpoint = 'https://api.openai.com/v1/embeddings';
7455 + $provider_name = 'OpenAI';
7456 + //error_log('[MXCHAT-EMBED] Using OpenAI API');
7457 + }
7458 +
7459 + //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
7460 +
7461 + if (empty($api_key)) {
7462 + $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
7463 + //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
7464 + return $error_message;
7465 + }
7466 +
7467 + // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
7468 + $estimated_tokens = ceil(str_word_count($text) / 0.75);
7469 + //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
7470 +
7471 + if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
7472 + //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
7473 + // Consider truncating text here
7474 + }
7475 +
7476 + // Prepare request body based on provider
7477 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7478 + // Gemini API format
7479 + $request_body = array(
7480 + 'model' => 'models/' . $selected_model,
7481 + 'content' => array(
7482 + 'parts' => array(
7483 + array('text' => $text)
7484 + )
7485 + )
7486 + );
7487 +
7488 + // Set output dimensionality to 1536 for consistency with other models
7489 + $request_body['outputDimensionality'] = 1536;
7490 + } else {
7491 + // OpenAI/Voyage API format
7492 + $request_body = array(
7493 + 'model' => $selected_model,
7494 + 'input' => $text
7495 + );
7496 +
7497 + // Add output_dimension for voyage-3-large model
7498 + if ($selected_model === 'voyage-3-large') {
7499 + $request_body['output_dimension'] = 2048;
7500 + }
7501 + }
7502 +
7503 + //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
7504 +
7505 + // Prepare headers based on provider
7506 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7507 + // Gemini uses API key as query parameter
7508 + $endpoint .= '?key=' . $api_key;
7509 + $headers = array(
7510 + 'Content-Type' => 'application/json'
7511 + );
7512 + } else {
7513 + // OpenAI/Voyage use Bearer token
7514 + $headers = array(
7515 + 'Authorization' => 'Bearer ' . $api_key,
7516 + 'Content-Type' => 'application/json'
7517 + );
7518 + }
7519 +
7520 + // Make API request
7521 + //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
7522 + $response = wp_remote_post($endpoint, array(
7523 + 'body' => wp_json_encode($request_body),
7524 + 'headers' => $headers,
7525 + 'timeout' => 60 // Increased timeout for large inputs
7526 + ));
7527 +
7528 + // Handle wp_remote_post errors
7529 + if (is_wp_error($response)) {
7530 + $error_message = $response->get_error_message();
7531 + //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
7532 + return 'Connection error: ' . $error_message;
7533 + }
7534 +
7535 + // Get and check HTTP response code
7536 + $http_code = wp_remote_retrieve_response_code($response);
7537 + //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
7538 +
7539 + if ($http_code !== 200) {
7540 + $error_body = wp_remote_retrieve_body($response);
7541 + //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
7542 +
7543 + // Try to parse error for more details
7544 + $error_json = json_decode($error_body, true);
7545 + if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
7546 + $error_type = $error_json['error']['type'] ?? 'unknown';
7547 + $error_message = $error_json['error']['message'] ?? 'No message';
7548 + //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
7549 + //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
7550 +
7551 + // Customize error message for common API errors
7552 + if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
7553 + $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
7554 + } elseif ($error_type === 'authentication_error') {
7555 + $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
7556 + }
7557 +
7558 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7559 + return $error_message;
7560 + }
7561 +
7562 + $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
7563 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7564 + return $error_message;
7565 + }
7566 +
7567 + // Parse response body
7568 + $response_body = wp_remote_retrieve_body($response);
7569 + //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
7570 +
7571 + $response_data = json_decode($response_body, true);
7572 +
7573 + if (json_last_error() !== JSON_ERROR_NONE) {
7574 + $error = json_last_error_msg();
7575 + //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
7576 + //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
7577 + return "Failed to parse API response: $error";
7578 + }
7579 +
7580 + // Handle different response formats based on provider
7581 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7582 + // Gemini API response format
7583 + if (isset($response_data['embedding']['values'])) {
7584 + $embedding_dimensions = count($response_data['embedding']['values']);
7585 + //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
7586 +
7587 + // Check if embedding dimensions are as expected (should be 1536)
7588 + if ($embedding_dimensions !== 1536) {
7589 + //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
7590 + }
7591 +
7592 + return $response_data['embedding']['values'];
7593 + } else {
7594 + //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
7595 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7596 +
7597 + if (isset($response_data['error'])) {
7598 + $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
7599 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7600 + return $error_message;
7601 + }
7602 +
7603 + $error_message = "Invalid Gemini API response format: No embedding found";
7604 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7605 + return $error_message;
7606 + }
7607 + } else {
7608 + // OpenAI/Voyage API response format
7609 + if (isset($response_data['data'][0]['embedding'])) {
7610 + $embedding_dimensions = count($response_data['data'][0]['embedding']);
7611 + //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
7612 +
7613 + // Check if embedding dimensions are as expected
7614 + if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
7615 + ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
7616 + //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
7617 + }
7618 +
7619 + return $response_data['data'][0]['embedding'];
7620 + } else {
7621 + //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
7622 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7623 +
7624 + if (isset($response_data['error'])) {
7625 + $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
7626 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7627 + return $error_message;
7628 + }
7629 +
7630 + $error_message = "Invalid API response format: No embedding found";
7631 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7632 + return $error_message;
7633 + }
7634 + }
7635 + }
5840 7636
5841 7637
5842 7638
5843 7639 }