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