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 +2960 -1093 2.0.52.1.5 View file →
@@ -37,11 +37,12 @@
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'));
@@ -56,8 +57,12 @@
56 57 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
57 58 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
58 59 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
59 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 +
60 65 }
61 66
62 67 // Method to check if the license is active
63 68 private function is_license_active() {
@@ -71,8 +76,11 @@
71 76 'api_key' => '',
72 77 'xai_api_key' => '',
73 78 'claude_api_key' => '',
74 79 'deepseek_api_key' => '',
80 + 'voyage_api_key' => '',
81 + 'gemini_api_key' => '',
82 + 'embedding_model' => 'text-embedding-ada-002',
75 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:
76 84 - Your name is [Chatbot Name].
77 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.
78 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.
@@ -82,12 +90,13 @@
82 90 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
83 91 'role_rate_limits' => array(),
84 92 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
85 93 'enable_email_block' => '',
86 - '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'),
87 95 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
88 96 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
89 - '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'),
90 99 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
91 100 'append_to_body' => esc_html__('off', 'mxchat'),
92 101 'close_button_color' => esc_html__('#fff', 'mxchat'),
93 102 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
@@ -115,8 +124,10 @@
115 124 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
116 125 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
117 126 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
118 127 'pdf_max_pages' => 69,
128 + 'show_pdf_upload_button' => 'on',
129 + 'show_word_upload_button' => 'on',
119 130
120 131 // Live Agent Integration
121 132 'live_agent_webhook_url' => '',
122 133 'live_agent_secret_key' => '',
@@ -185,13 +196,13 @@
185 196 );
186 197
187 198 add_submenu_page(
188 199 'mxchat-max',
189 - esc_html__('MxChat Intents', 'mxchat'),
190 - esc_html__('Intents', 'mxchat'),
200 + esc_html__('MxChat Actions', 'mxchat'),
201 + esc_html__('Actions', 'mxchat'),
191 202 'manage_options',
192 - 'mxchat-intents',
193 - array($this, 'mxchat_intents_page_html')
203 + 'mxchat-actions',
204 + array($this, 'mxchat_actions_page_html')
194 205 );
195 206
196 207 add_submenu_page(
197 208 'mxchat-max',
@@ -223,16 +234,20 @@
223 234 check_ajax_referer('mxchat_save_setting_nonce');
224 235 if (!current_user_can('manage_options')) {
225 236 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
226 237 }
238 +
227 239 $name = isset($_POST['name']) ? $_POST['name'] : '';
228 240 // Strip slashes from the value before saving
229 241 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
242 +
230 243 if (empty($name)) {
231 244 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
232 245 }
246 +
233 247 // Load the full options array
234 248 $options = get_option('mxchat_options', []);
249 +
235 250 // Handle special cases
236 251 switch ($name) {
237 252 case 'additional_popular_questions':
238 253 $questions = json_decode($value, true); // No need for stripslashes here
@@ -241,8 +256,16 @@
241 256 // Also update old option for backwards compatibility
242 257 update_option('additional_popular_questions', $questions);
243 258 }
244 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;
245 268 case 'user_message_bg_color':
246 269 case 'user_message_font_color':
247 270 case 'bot_message_bg_color':
248 271 case 'bot_message_font_color':
@@ -287,9 +310,11 @@
287 310 if (strpos($name, 'toggle') !== false || in_array($name, [
288 311 'chat_persistence_toggle',
289 312 'privacy_toggle',
290 313 'complianz_toggle',
291 - 'chat_toolbar_toggle' // Removed live_agent_status from here
314 + 'chat_toolbar_toggle',
315 + 'show_pdf_upload_button',
316 + 'show_word_upload_button'
292 317 ])) {
293 318 $options[$name] = ($value === 'on') ? 'on' : 'off';
294 319 } else {
295 320 // Store all other values directly
@@ -296,22 +321,44 @@
296 321 $options[$name] = $value;
297 322 }
298 323 break;
299 324 }
300 - // Save all updates
325 +
326 + // Save all updates to the options array
301 327 $updated = update_option('mxchat_options', $options);
302 - // Handle backwards compatibility for certain fields
303 - $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
304 - 'brave_country', 'brave_language', 'similarity_threshold'];
305 - if (in_array($name, $legacy_fields)) {
306 - // Also update the individual option for backwards compatibility
307 - 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;
308 343 }
309 - if ($updated) {
310 - wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
311 - } else {
312 - 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);
313 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;
314 361 }
315 362
316 363 /**
317 364 * Handles AJAX auto-save for prompts and auto-sync settings.
@@ -316,30 +363,41 @@
316 363 /**
317 364 * Handles AJAX auto-save for prompts and auto-sync settings.
318 365 */
319 366 public function mxchat_save_prompts_setting_callback() {
320 - // Check the correct nonce for this action.
321 - check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
322 -
323 - if ( ! current_user_can( 'manage_options' ) ) {
324 - 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')]);
325 371 }
326 -
327 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
328 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
329 -
330 - if ( empty( $name ) ) {
331 - 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')]);
332 378 }
333 -
334 - // Handle standalone auto-sync settings.
335 - if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
336 - // Convert checkbox value to "1" if checked, "0" if not.
337 - $value = ( $value === 'on' ) ? '1' : '0';
338 - update_option( $name, $value );
339 - 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;
340 398 }
341 -
399 +
342 400 // Handle fields stored in the 'mxchat_prompts_options' array.
343 401 // Handle fields stored in the 'mxchat_prompts_options' array.
344 402 if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
345 403 // Extract the key name using regex.
@@ -365,9 +423,8 @@
365 423
366 424
367 425 wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
368 426 }
369 -
370 427 public function mxchat_display_admin_notice() {
371 428 // Success notice
372 429 if ($message = get_transient('mxchat_admin_notice_success')) {
373 430 ?>
@@ -397,181 +454,215 @@
397 454
398 455 public function mxchat_create_admin_page() {
399 456
400 457 ?>
401 - <div class="wrap mxchat-admin">
402 - <?php if (!$this->is_activated): ?>
403 - <div class="mxchat-pro-banner">
404 - <p>
405 - <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
406 - <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
407 - </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>
408 467 </div>
409 - <?php endif; ?>
410 468
411 - <div class="mxchat-agents-banner">
412 - <p>
413 - <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'); ?>
414 - </p>
415 - </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; ?>
416 484
417 - <h2 class="mxchat-nav-tab-wrapper">
418 - <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
419 - <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
420 - <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
421 - </h2>
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>
490 + </div>
422 491
423 - <div id="chatbot" class="mxchat-tab-content active">
424 - <div class="mxchat-autosave-section">
425 - <?php do_settings_sections('mxchat-chatbot'); ?>
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>
498 + </div>
426 499 </div>
427 - </div>
428 500
429 - <div id="embed" class="mxchat-tab-content">
430 - <div class="mxchat-autosave-section">
431 -
432 - <div class="mxchat-settings-section">
433 - <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
434 - <table class="form-table">
435 - <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
436 - </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>
437 509 </div>
438 510
439 - <div class="section-divider"></div>
440 -
441 - <div class="mxchat-settings-section">
442 - <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
443 - <table class="form-table">
444 - <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
445 - </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>
446 518 </div>
447 519
448 - <div class="section-divider"></div>
449 -
450 - <div class="mxchat-settings-section">
451 - <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
452 - <table class="form-table">
453 - <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
454 - </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>
455 527 </div>
456 528
457 - <div class="section-divider"></div>
458 -
459 - <!-- Live Agent Settings Section -->
460 - <div class="mxchat-settings-section">
461 - <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
462 - <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>
463 - <table class="form-table">
464 - <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
465 - </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>
466 537 </div>
467 538 </div>
468 - </div>
469 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">
470 544
471 - <div id="general" class="mxchat-tab-content">
472 - <?php do_settings_sections('mxchat-general'); ?>
473 - <p>
474 - <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
475 - <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
476 - <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
477 - </p>
478 - <p>
479 - <?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>.
480 - </p>
481 -
482 - <div class="faq-item">
483 - <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
484 - <p>
485 - <?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'); ?>
486 - </p>
487 - <p>
488 - <?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'); ?>
489 - </p>
490 - <p>
491 - <?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>.
492 - </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>
493 554 </div>
555 + </div>
494 556
495 - <div class="faq-item">
496 - <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
497 - <p>
498 - <?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'); ?>
499 - </p>
500 - <p>
501 - <?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>.
502 - </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>
503 565 </div>
566 + </div>
504 567
505 - <div class="faq-item">
506 - <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
507 - <p>
508 - <?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'); ?>
509 - </p>
510 - <p>
511 - <?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'); ?>
512 - </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>
513 576 </div>
577 + </div>
514 578
515 - <div class="faq-item">
516 - <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
517 - <p>
518 - <?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'); ?>
519 - </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>
520 588 </div>
589 + </div>
521 590
522 - <div class="faq-item">
523 - <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
524 - <p>
525 - <?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'); ?>
526 - </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>
527 599 </div>
528 -
529 - <div class="faq-item">
530 - <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
531 - <p>
532 - <?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'); ?>
533 - </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>
534 610 </div>
611 + </div>
535 612
536 - <div class="faq-item">
537 - <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
538 - <p>
539 - <?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>.
540 - </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>
541 621 </div>
622 + </div>
542 623
543 - <div class="faq-item">
544 - <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
545 - <p>
546 - <?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'); ?>
547 - </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>
548 633 </div>
634 + </div>
549 635
550 - <div class="faq-item">
551 - <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
552 - <p>
553 - <?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'); ?>
554 - </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>
555 645 </div>
646 + </div>
647 + </div>
556 648
557 - <div class="faq-item">
558 - <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
559 - <p>
560 - <?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'); ?>
561 - </p>
562 - <p>
563 - <?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'); ?>
564 - </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>
565 664 </div>
566 -
567 - <div class="faq-item">
568 - <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
569 - <p>
570 - <?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'); ?>
571 - </p>
572 - </div>
573 -
574 665 </div>
575 666 </div>
576 667 <?php
577 668 }
@@ -998,10 +1089,39 @@
998 1089 <div class="mxchat-content">
999 1090 <!-- Import Settings Card -->
1000 1091 <div class="mxchat-card">
1001 1092 <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
1002 - <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']);
1003 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 +
1004 1124 <!-- Tab Contents -->
1005 1125 <div class="mxchat-tab-contents">
1006 1126 <!-- Default Database Tab -->
1007 1127 <div id="default-db" class="mxchat-tab-content active">
@@ -1026,8 +1146,9 @@
1026 1146 <span class="mxchat-toggle-label">
1027 1147 <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
1028 1148 </span>
1029 1149 </div>
1150 +
1030 1151 <div class="mxchat-toggle-container">
1031 1152 <label class="mxchat-toggle-switch">
1032 1153 <input type="checkbox"
1033 1154 name="mxchat_auto_sync_pages"
@@ -1040,8 +1161,64 @@
1040 1161 <span class="mxchat-toggle-label">
1041 1162 <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
1042 1163 </span>
1043 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 +
1044 1221 </div>
1045 1222 </div>
1046 1223 </div>
1047 1224
@@ -1048,10 +1225,9 @@
1048 1225 <!-- Import Methods -->
1049 1226 <div class="mxchat-import-methods">
1050 1227 <div class="mxchat-method-card">
1051 1228 <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1052 - <p><?php esc_html_e('Import all content from your sitemap automatically. Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
1053 - </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>
1054 1230 <div class="mxchat-method-card">
1055 1231 <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1056 1232 <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
1057 1233 </div>
@@ -1087,85 +1263,177 @@
1087 1263 </div>
1088 1264 <?php endif; ?>
1089 1265
1090 1266 <!-- Processing Status -->
1091 - <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
1092 - <div class="mxchat-status-card">
1093 - <div class="mxchat-status-header">
1094 - <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1095 - <?php if ($is_processing) : ?>
1096 - <form method="post" class="mxchat-stop-form"
1097 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1098 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1099 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1100 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1101 - </button>
1102 - </form>
1103 - <?php endif; ?>
1104 - </div>
1105 - <div class="mxchat-progress-bar">
1106 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1107 - </div>
1108 - <div class="mxchat-status-details">
1109 - <p><?php printf(
1110 - esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1111 - absint($pdf_status['processed_pages']),
1112 - absint($pdf_status['total_pages']),
1113 - absint($pdf_status['percentage'])
1114 - ); ?></p>
1115 - <p><?php printf(
1116 - esc_html__('Status: %s', 'mxchat'),
1117 - esc_html(ucfirst($pdf_status['status']))
1118 - ); ?></p>
1119 - <p><?php printf(
1120 - esc_html__('Last update: %s', 'mxchat'),
1121 - esc_html($pdf_status['last_update'])
1122 - ); ?></p>
1123 - </div>
1124 - </div>
1125 - <?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; ?>
1126 1312
1127 - <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
1128 - <div class="mxchat-status-card">
1129 - <div class="mxchat-status-header">
1130 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
1131 - <?php if ($is_processing) : ?>
1132 - <form method="post" class="mxchat-stop-form"
1133 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1134 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1135 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1136 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1137 - </button>
1138 - </form>
1139 - <?php endif; ?>
1140 - </div>
1141 - <div class="mxchat-progress-bar">
1142 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
1143 - </div>
1144 - <div class="mxchat-status-details">
1145 - <p><?php printf(
1146 - esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
1147 - absint($sitemap_status['processed_urls']),
1148 - absint($sitemap_status['total_urls']),
1149 - absint($sitemap_status['percentage'])
1150 - ); ?></p>
1151 - <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
1152 - <div class="mxchat-error-notice">
1153 - <?php if (!empty($sitemap_status['error'])) : ?>
1154 - <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
1155 - <?php endif; ?>
1156 - <?php if (!empty($sitemap_status['last_error'])) : ?>
1157 - <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
1158 - <?php endif; ?>
1159 - </div>
1160 - <?php endif; ?>
1161 - </div>
1162 - </div>
1163 - <?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>
1164 1345 </div>
1346 + <?php endif; ?>
1165 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>
1166 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; ?>
1167 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 +
1168 1436 </div>
1169 1437
1170 1438 <!-- Direct Content Submission Card -->
1171 1439 <div class="mxchat-card">
@@ -1247,16 +1515,19 @@
1247 1515 <?php echo esc_textarea($prompt->article_content); ?>
1248 1516 </textarea>
1249 1517 </td>
1250 1518 <td class="mxchat-url-cell">
1251 - <?php if (!empty($prompt->source_url)) : ?>
1252 - <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
1253 - <span class="dashicons dashicons-external"></span>
1254 - <?php esc_html_e('View Source', 'mxchat'); ?>
1255 - </a>
1256 - <?php else : ?>
1257 - <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
1258 - <?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); ?>" />
1259 1530 </td>
1260 1531 <td class="mxchat-actions-cell">
1261 1532 <button class="mxchat-button-icon edit-button"
1262 1533 data-id="<?php echo esc_attr($prompt->id); ?>">
@@ -1358,30 +1629,154 @@
1358 1629
1359 1630 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1360 1631 exit;
1361 1632 }
1362 -public function mxchat_generate_embedding($text) {
1363 - $options = get_option('mxchat_options');
1364 - $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1365 1633
1366 - $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1367 - 'body' => wp_json_encode(array(
1368 - 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1369 - 'input' => $text
1370 - )),
1371 - 'headers' => array(
1372 - 'Authorization' => 'Bearer ' . $api_key,
1373 - 'Content-Type' => esc_html__('application/json', 'mxchat')
1374 - ),
1375 - ));
1376 1634
1377 - if (is_wp_error($response)) {
1378 - 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;
1379 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 +}
1380 1777
1381 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1382 - return $response_data['data'][0]['embedding'] ?? null;
1383 - }
1778 +
1384 1779 public function mxchat_delete_chat_history() {
1385 1780 if (!current_user_can('manage_options')) {
1386 1781 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1387 1782 wp_die();
@@ -1469,45 +1864,106 @@
1469 1864 }
1470 1865
1471 1866
1472 1867
1473 -// 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 + */
1474 1891 public function handle_post_update($post_id, $post, $update) {
1475 1892 // Basic validation checks
1476 1893 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1477 1894 return;
1478 1895 }
1479 -
1896 +
1480 1897 // Only process published content
1481 - if (!in_array($post->post_status, array('publish'))) {
1898 + if ($post->post_status !== 'publish') {
1482 1899 return;
1483 1900 }
1484 -
1901 +
1902 + $post_type = $post->post_type;
1903 +
1485 1904 // Check if sync is enabled for this post type
1486 - $post_type = $post->post_type;
1487 - if (!in_array($post_type, array('post', 'page'))) {
1488 - 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 + }
1489 1918 }
1490 -
1491 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1492 - if (get_option($sync_option) != '1') {
1919 +
1920 + if (!$should_sync) {
1493 1921 return;
1494 1922 }
1495 -
1923 +
1496 1924 // Prepare content and URL
1497 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 +
1498 1955 $url = get_permalink($post_id);
1499 -
1956 +
1500 1957 // Generate embedding vector
1501 1958 $embedding_vector = $this->mxchat_generate_embedding($content);
1502 1959 if (!$embedding_vector) {
1503 1960 return;
1504 1961 }
1505 -
1962 +
1506 1963 // Check for Pinecone addon and its settings
1507 1964 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1508 1965 $use_pinecone = false;
1509 -
1510 1966 if ($pinecone_settings && is_array($pinecone_settings)) {
1511 1967 // Check if Pinecone is enabled and all required settings are present
1512 1968 $use_pinecone = (
1513 1969 isset($pinecone_settings['mxchat_use_pinecone']) &&
@@ -1517,9 +1973,9 @@
1517 1973 !empty($pinecone_settings['mxchat_pinecone_index']) &&
1518 1974 !empty($pinecone_settings['mxchat_pinecone_environment'])
1519 1975 );
1520 1976 }
1521 -
1977 +
1522 1978 if ($use_pinecone) {
1523 1979 // Use Pinecone
1524 1980 $pinecone_result = $this->store_in_pinecone_main(
1525 1981 $embedding_vector,
@@ -1528,11 +1984,10 @@
1528 1984 $pinecone_settings['mxchat_pinecone_api_key'],
1529 1985 $pinecone_settings['mxchat_pinecone_environment'],
1530 1986 $pinecone_settings['mxchat_pinecone_index']
1531 1987 );
1532 -
1533 1988 if (!$pinecone_result['success']) {
1534 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1989 + // Fallback to WordPress DB
1535 1990 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1536 1991 }
1537 1992 } else {
1538 1993 // Fallback to WordPress DB storage
@@ -1537,9 +1992,122 @@
1537 1992 } else {
1538 1993 // Fallback to WordPress DB storage
1539 1994 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1540 1995 }
1996 +
1541 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 +
1542 2110 // Modified storage function to add type field
1543 2111 private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1544 2112 $vector_id = $vector_id ?: md5($url);
1545 2113 $options = get_option('mxchat_pinecone_addon_options');
@@ -1606,8 +2174,10 @@
1606 2174 'success' => true,
1607 2175 'message' => 'Successfully stored in Pinecone'
1608 2176 );
1609 2177 }
2178 +
2179 +
1610 2180 // Helper method for WordPress DB storage
1611 2181 private function store_in_wordpress_db($content, $url, $embedding_vector) {
1612 2182 global $wpdb;
1613 2183 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
@@ -1639,103 +2209,10 @@
1639 2209 array('%s', '%s', '%s', '%s')
1640 2210 );
1641 2211 }
1642 2212 }
1643 -/**
1644 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1645 - *
1646 - * @param int $post_id The ID of the post being deleted
1647 - * @return void
1648 - */
1649 -public function mxchat_handle_post_delete($post_id) {
1650 - // Get post data before it's deleted
1651 - $post = get_post($post_id);
1652 2213
1653 - // Basic validation
1654 - if (!$post || wp_is_post_revision($post_id)) {
1655 - return;
1656 - }
1657 2214
1658 - // Check post type
1659 - $post_type = $post->post_type;
1660 - if (!in_array($post_type, array('post', 'page'))) {
1661 - return;
1662 - }
1663 -
1664 - // Check if sync is enabled for this post type
1665 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1666 - if (get_option($sync_option) != '1') {
1667 - return;
1668 - }
1669 -
1670 - // Get the URL before post is deleted
1671 - $source_url = get_permalink($post_id);
1672 - if (!$source_url) {
1673 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1674 - return;
1675 - }
1676 -
1677 - // Check for Pinecone addon and its settings
1678 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1679 - $use_pinecone = false;
1680 - if ($pinecone_settings && is_array($pinecone_settings)) {
1681 - $use_pinecone = (
1682 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1683 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1684 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1685 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1686 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1687 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1688 - );
1689 - }
1690 -
1691 - $deletion_successful = false;
1692 -
1693 - if ($use_pinecone) {
1694 - try {
1695 - $pinecone_result = $this->delete_from_pinecone(
1696 - array($source_url),
1697 - $pinecone_settings['mxchat_pinecone_api_key'],
1698 - $pinecone_settings['mxchat_pinecone_environment'],
1699 - $pinecone_settings['mxchat_pinecone_index']
1700 - );
1701 -
1702 - if (!$pinecone_result['success']) {
1703 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1704 - } else {
1705 - $deletion_successful = true;
1706 - }
1707 - } catch (Exception $e) {
1708 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1709 - }
1710 - }
1711 -
1712 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1713 - try {
1714 - global $wpdb;
1715 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1716 -
1717 - $result = $wpdb->delete(
1718 - $table_name,
1719 - array('source_url' => $source_url),
1720 - array('%s')
1721 - );
1722 -
1723 - if ($result === false) {
1724 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1725 - } else {
1726 - $deletion_successful = true;
1727 - }
1728 - } catch (Exception $e) {
1729 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1730 - }
1731 -
1732 - if (!$deletion_successful) {
1733 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1734 - }
1735 -}
1736 -
1737 -
1738 2215 public function mxchat_handle_content_submission() {
1739 2216 // Check if the form was submitted and the user has permission.
1740 2217 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1741 2218 return;
@@ -1907,31 +2384,59 @@
1907 2384 $start_page = absint($status['processed_pages']);
1908 2385 $end_page = min($start_page + $batch_size, $total_pages);
1909 2386
1910 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 + }
1911 2393
1912 2394 for ($i = $start_page; $i < $end_page; $i++) {
1913 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 +
1914 2403 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1915 2404
1916 - if (!empty($sanitized_content)) {
1917 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1918 - if (is_array($embedding_vector)) {
1919 - $metadata = array(
1920 - 'document_type' => 'pdf',
1921 - 'total_pages' => $total_pages,
1922 - 'current_page' => $i + 1,
1923 - 'prev_page' => $i > 0 ? $i : null,
1924 - 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1925 - 'source_url' => $pdf_url
1926 - );
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 + }
1927 2410
1928 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1929 - $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 + );
1930 2428
1931 - $options = get_option('mxchat_options');
1932 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1933 - }
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()));
1934 2439 }
1935 2440
1936 2441 // Update progress with sanitized data
1937 2442 $status['processed_pages'] = absint($i + 1);
@@ -1957,16 +2462,36 @@
1957 2462 }
1958 2463 }
1959 2464
1960 2465 } catch (\Exception $e) {
1961 - $status['status'] = 'error';
1962 - $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 +
1963 2486 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2487 +
1964 2488 if (file_exists($pdf_path)) {
1965 2489 wp_delete_file($pdf_path);
1966 2490 }
1967 2491 }
1968 2492 }
2493 +
1969 2494 public function get_pdf_processing_status($pdf_url) {
1970 2495 $pdf_url = esc_url_raw($pdf_url);
1971 2496 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1972 2497
@@ -1973,29 +2498,57 @@
1973 2498 if (!$status || !is_array($status)) {
1974 2499 return false;
1975 2500 }
1976 2501
1977 - 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(
1978 2516 'total_pages' => absint($status['total_pages']),
1979 2517 'processed_pages' => absint($status['processed_pages']),
1980 2518 'percentage' => ($status['total_pages'] > 0)
1981 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1982 - : 0,
2519 + ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
2520 + : 0,
1983 2521 'status' => sanitize_text_field($status['status']),
1984 2522 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1985 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;
1986 2531 }
2532 +
2533 +
1987 2534 public function mxchat_handle_sitemap_submission() {
2535 + // Start logging the submission process
2536 + //error_log('[MXCHAT-URL] ===== Starting URL submission process =====');
2537 +
1988 2538 // Check if the form was submitted and verify permissions
1989 2539 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
2540 + //error_log('[MXCHAT-URL] Error: Unauthorized access or form not submitted properly');
1990 2541 wp_die(esc_html__('Unauthorized access', 'mxchat'));
1991 2542 }
1992 2543
1993 2544 // Verify nonce
2545 + //error_log('[MXCHAT-URL] Verifying nonce');
1994 2546 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1995 2547
1996 2548 // Validate URL
1997 2549 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
2550 + //error_log('[MXCHAT-URL] Error: Empty or missing URL');
1998 2551 set_transient('mxchat_admin_notice_error',
1999 2552 esc_html__('Please provide a valid URL.', 'mxchat'),
2000 2553 30
2001 2554 );
@@ -2003,12 +2556,40 @@
2003 2556 exit;
2004 2557 }
2005 2558
2006 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');
2007 2587 $response = wp_remote_get($submitted_url, array('timeout' => 30));
2008 2588
2009 2589 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
2010 - $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);
2011 2592 set_transient('mxchat_admin_notice_error',
2012 2593 sprintf(
2013 2594 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
2014 2595 esc_html($error_message)
@@ -2019,11 +2600,13 @@
2019 2600 exit;
2020 2601 }
2021 2602
2022 2603 $content_type = wp_remote_retrieve_header($response, 'content-type');
2604 + //error_log('[MXCHAT-URL] Content type: ' . $content_type);
2023 2605 $body_content = wp_remote_retrieve_body($response);
2024 2606
2025 2607 if (empty($body_content)) {
2608 + //error_log('[MXCHAT-URL] Error: Empty response body');
2026 2609 set_transient('mxchat_admin_notice_error',
2027 2610 esc_html__('Empty response received from URL.', 'mxchat'),
2028 2611 30
2029 2612 );
@@ -2029,12 +2612,15 @@
2029 2612 );
2030 2613 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2031 2614 exit;
2032 2615 }
2616 + //error_log('[MXCHAT-URL] Retrieved body content length: ' . strlen($body_content) . ' bytes');
2033 2617
2034 2618 // Handle PDF URL
2035 2619 if ($this->is_pdf_url($submitted_url, $response)) {
2620 + //error_log('[MXCHAT-URL] Detected PDF URL, handling PDF for knowledge base');
2036 2621 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
2622 + //error_log('[MXCHAT-URL] PDF handling result: ' . $result);
2037 2623
2038 2624 if ($result === 'scheduled') {
2039 2625 set_transient(
2040 2626 'mxchat_last_pdf_url',
@@ -2046,9 +2632,9 @@
2046 2632 30
2047 2633 );
2048 2634 } else {
2049 2635 set_transient('mxchat_admin_notice_error',
2050 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
2636 + esc_html__('Failed to start PDF processing: ', 'mxchat') . esc_html($result),
2051 2637 30
2052 2638 );
2053 2639 }
2054 2640
@@ -2057,8 +2643,9 @@
2057 2643 }
2058 2644
2059 2645 // Handle Sitemap XML
2060 2646 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
2647 + //error_log('[MXCHAT-URL] Detected XML content, processing as sitemap');
2061 2648 libxml_use_internal_errors(true);
2062 2649 $xml = simplexml_load_string($body_content);
2063 2650 $xml_errors = libxml_get_errors();
2064 2651 libxml_clear_errors();
@@ -2063,8 +2650,15 @@
2063 2650 $xml_errors = libxml_get_errors();
2064 2651 libxml_clear_errors();
2065 2652
2066 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 +
2067 2661 set_transient('mxchat_admin_notice_error',
2068 2662 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
2069 2663 30
2070 2664 );
@@ -2071,9 +2665,11 @@
2071 2665 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2072 2666 exit;
2073 2667 }
2074 2668
2669 + //error_log('[MXCHAT-URL] Valid XML found, handling sitemap for knowledge base');
2075 2670 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
2671 + //error_log('[MXCHAT-URL] Sitemap handling result: ' . $result);
2076 2672
2077 2673 if ($result === 'scheduled') {
2078 2674 set_transient(
2079 2675 'mxchat_last_sitemap_url',
@@ -2084,10 +2680,12 @@
2084 2680 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
2085 2681 30
2086 2682 );
2087 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
2088 2686 set_transient('mxchat_admin_notice_error',
2089 - 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'),
2090 2688 30
2091 2689 );
2092 2690 }
2093 2691
@@ -2095,42 +2693,147 @@
2095 2693 exit;
2096 2694 }
2097 2695
2098 2696 // Handle Regular URL
2099 - $page_content = $this->mxchat_extract_main_content($body_content);
2100 - $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');
2101 2701
2102 - if (empty($sanitized_content)) {
2103 - set_transient('mxchat_admin_notice_error',
2104 - esc_html__('No valid content found on the provided URL.', 'mxchat'),
2105 - 30
2106 - );
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 +
2107 2775 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2108 2776 exit;
2109 2777 }
2110 2778
2111 - $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2112 - if (is_array($embedding_vector)) {
2113 - MxChat_Utils::submit_content_to_db(
2114 - $sanitized_content,
2115 - $submitted_url,
2116 - $this->options['api_key']
2117 - );
2118 - set_transient('mxchat_admin_notice_success',
2119 - esc_html__('URL content successfully submitted!', 'mxchat'),
2120 - 30
2121 - );
2122 - } else {
2123 - set_transient('mxchat_admin_notice_error',
2124 - esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
2125 - 30
2126 - );
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;
2127 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 +}
2128 2832
2129 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2130 - exit;
2131 -}
2132 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');
2133 2836 if (!current_user_can('manage_options')) {
2134 2837 //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
2135 2838 return false;
2136 2839 }
@@ -2141,8 +2844,40 @@
2141 2844 if (!$xml || !is_object($xml)) {
2142 2845 throw new Exception(__('Invalid XML object provided', 'mxchat'));
2143 2846 }
2144 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 +
2145 2880 $urls = [];
2146 2881 foreach ($xml->url as $url_element) {
2147 2882 $url = esc_url_raw((string)$url_element->loc);
2148 2883 if ($url) {
@@ -2179,12 +2914,36 @@
2179 2914
2180 2915 return __('scheduled', 'mxchat');
2181 2916
2182 2917 } catch (\Exception $e) {
2183 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
2184 - 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;
2185 2943 }
2186 2944 }
2945 +
2187 2946 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
2188 2947 // Validate inputs
2189 2948 $sitemap_url = esc_url_raw($sitemap_url);
2190 2949 $total_urls = absint($total_urls);
@@ -2202,8 +2961,13 @@
2202 2961 if (!$status || !is_array($status)) {
2203 2962 throw new Exception('Invalid status data retrieved from transient');
2204 2963 }
2205 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 +
2206 2970 $start_url = absint($status['processed_urls']);
2207 2971 $end_url = min($start_url + $batch_size, $total_urls);
2208 2972 $instance = new self();
2209 2973
@@ -2210,11 +2974,38 @@
2210 2974 // Track failures in batch
2211 2975 $batch_stats = [
2212 2976 'processed' => 0,
2213 2977 'failed' => 0,
2214 - 'last_error' => ''
2978 + 'last_error' => '',
2979 + 'embedding_errors' => 0 // Track specifically embedding errors
2215 2980 ];
2216 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 +
2217 3008 for ($i = $start_url; $i < $end_url; $i++) {
2218 3009 $page_url = esc_url_raw($urls[$i]);
2219 3010 $page_response = wp_remote_get($page_url);
2220 3011
@@ -2219,8 +3010,18 @@
2219 3010 $page_response = wp_remote_get($page_url);
2220 3011
2221 3012 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
2222 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 +
2223 3024 continue;
2224 3025 }
2225 3026
2226 3027 $page_html = wp_remote_retrieve_body($page_response);
@@ -2229,8 +3030,29 @@
2229 3030
2230 3031 if (!empty($sanitized_content)) {
2231 3032 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2232 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)
2233 3055 if (is_array($embedding_vector)) {
2234 3056 $options = get_option('mxchat_options');
2235 3057 $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2236 3058
@@ -2236,8 +3058,16 @@
2236 3058
2237 3059 if (is_wp_error($submission_result)) {
2238 3060 $batch_stats['failed']++;
2239 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 +
2240 3070 continue;
2241 3071 }
2242 3072
2243 3073 $batch_stats['processed']++;
@@ -2242,9 +3072,22 @@
2242 3072
2243 3073 $batch_stats['processed']++;
2244 3074 } else {
2245 3075 $batch_stats['failed']++;
2246 - $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 + }
2247 3090 }
2248 3091 }
2249 3092
2250 3093 $status['processed_urls'] = absint($i + 1);
@@ -2251,9 +3094,14 @@
2251 3094 $status['last_update'] = time();
2252 3095 $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
2253 3096 $status['last_error'] = $batch_stats['last_error'];
2254 3097
2255 - 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);
2256 3104 }
2257 3105
2258 3106 // If all URLs in this batch failed, stop processing
2259 3107 if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
@@ -2262,15 +3110,15 @@
2262 3110 'Processing stopped: %d consecutive failures. Last error: %s',
2263 3111 $batch_stats['failed'],
2264 3112 $batch_stats['last_error']
2265 3113 );
2266 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
3114 + set_transient($status_key, $status, DAY_IN_SECONDS);
2267 3115 return;
2268 3116 }
2269 3117
2270 3118 if ($end_url < $total_urls) {
2271 3119 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2272 - 'urls' => array_map('esc_url_raw', $urls),
3120 + 'urls' => $urls,
2273 3121 'sitemap_url' => $sitemap_url,
2274 3122 'total_urls' => $total_urls,
2275 3123 'batch_size' => $batch_size,
2276 3124 'batch_pause' => $batch_pause,
@@ -2276,17 +3124,249 @@
2276 3124 'batch_pause' => $batch_pause,
2277 3125 ));
2278 3126 } else {
2279 3127 $status['status'] = 'complete';
2280 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
3128 + set_transient($status_key, $status, DAY_IN_SECONDS);
2281 3129 }
2282 3130 } catch (\Exception $e) {
2283 3131 $status['status'] = 'error';
2284 - $status['error'] = sanitize_text_field($e->getMessage());
2285 - 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);
2286 3134 }
2287 3135 }
2288 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 +
2289 3369 public function get_sitemap_processing_status($sitemap_url) {
2290 3370 $sitemap_url = esc_url_raw($sitemap_url);
2291 3371 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2292 3372
@@ -2298,17 +3378,53 @@
2298 3378 'total_urls' => absint($status['total_urls']),
2299 3379 'processed_urls' => absint($status['processed_urls']),
2300 3380 'failed_urls' => absint($status['failed_urls'] ?? 0),
2301 3381 'percentage' => ($status['total_urls'] > 0)
2302 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2303 - : 0,
3382 + ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
3383 + : 0,
2304 3384 'status' => sanitize_text_field($status['status']),
2305 3385 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2306 3386 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2307 - '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()
2308 3389 );
2309 3390 }
3391 +public function ajax_get_status_updates() {
3392 + // Verify the request
3393 + check_ajax_referer('mxchat_status_nonce', 'nonce');
2310 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 +}
2311 3427 public function mxchat_stop_processing() {
2312 3428 // Verify permissions
2313 3429 if (!current_user_can('manage_options')) {
2314 3430 wp_die(esc_html__('Unauthorized access', 'mxchat'));
@@ -2344,27 +3460,10 @@
2344 3460 );
2345 3461 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2346 3462 exit;
2347 3463 }
2348 -private function mxchat_sanitize_content_for_api($content) {
2349 - // Remove script, style tags, and HTML comments
2350 - $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2351 - $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2352 - $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2353 3464
2354 - // Remove all HTML tags and decode HTML entities
2355 - $content = wp_strip_all_tags($content);
2356 - $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2357 3465
2358 - // Trim and normalize whitespace
2359 - $content = trim(preg_replace('/\s+/', ' ', $content));
2360 -
2361 - return $content;
2362 -}
2363 -
2364 -
2365 -
2366 -
2367 3466 public function mxchat_handle_product_change($post_id, $post, $update) {
2368 3467 if ($post->post_type !== 'product') {
2369 3468 return;
2370 3469 }
@@ -2377,9 +3476,8 @@
2377 3476 }
2378 3477 });
2379 3478 }
2380 3479 }
2381 -
2382 3480 private function mxchat_store_product_embedding($product) {
2383 3481 if (!isset($this->options['enable_woocommerce_integration']) ||
2384 3482 !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2385 3483 return;
@@ -2435,9 +3533,8 @@
2435 3533 // Use WordPress DB storage
2436 3534 $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2437 3535 }
2438 3536 }
2439 -
2440 3537 public function mxchat_handle_product_delete($post_id) {
2441 3538 if (get_post_type($post_id) !== 'product') {
2442 3539 return;
2443 3540 }
@@ -2624,9 +3721,9 @@
2624 3721 </div>
2625 3722 <?php
2626 3723 }
2627 3724
2628 -public function mxchat_intents_page_html() {
3725 +public function mxchat_actions_page_html() {
2629 3726 if (!current_user_can('manage_options')) {
2630 3727 return;
2631 3728 }
2632 3729
@@ -2639,9 +3736,9 @@
2639 3736
2640 3737 // Success message
2641 3738 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2642 3739 echo '<div class="notice notice-success is-dismissible"><p>' .
2643 - esc_html__('Intent updated successfully.', 'mxchat') .
3740 + esc_html__('Action updated successfully.', 'mxchat') .
2644 3741 '</p></div>';
2645 3742 }
2646 3743
2647 3744 // Filtering logic
@@ -2662,10 +3759,10 @@
2662 3759 // Pagination
2663 3760 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2664 3761 $total_pages = ceil($total_intents / $per_page);
2665 3762
2666 - // Get intents
2667 - $intents = $wpdb->get_results($wpdb->prepare(
3763 + // Get intents (now called actions)
3764 + $actions = $wpdb->get_results($wpdb->prepare(
2668 3765 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2669 3766 $per_page, $offset
2670 3767 ));
2671 3768
@@ -2670,441 +3767,604 @@
2670 3767 ));
2671 3768
2672 3769 // Get callbacks
2673 3770 $available_callbacks = $this->mxchat_get_available_callbacks();
2674 -
3771 +
2675 3772 ?>
2676 3773 <div class="wrap mxchat-wrapper">
2677 3774 <!-- Hero Section -->
2678 3775 <div class="mxchat-hero">
2679 3776 <h1 class="mxchat-main-title">
2680 - <span class="mxchat-gradient-text">Intent</span> Manager
3777 + <span class="mxchat-gradient-text">Actions</span> Manager
2681 3778 </h1>
2682 3779 <p class="mxchat-hero-subtitle">
2683 - <?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'); ?>
2684 3781 </p>
2685 3782 </div>
2686 3783
2687 - <div class="mxchat-content">
2688 - <!-- Add Intent Card -->
2689 - <div class="mxchat-card">
2690 - <div class="mxchat-card-header">
2691 - <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2692 - </div>
2693 -
2694 - <div class="mxchat-intent-documentation">
2695 - <p>
2696 - <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2697 - <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2698 - <?php esc_html_e('documentation', 'mxchat'); ?>
2699 - </a>
2700 - <?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'); ?>
2701 - </p>
2702 - </div>
2703 -
2704 - <form id="mxchat-add-intent-form" method="post"
2705 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2706 - <input type="hidden" name="action" value="mxchat_add_intent">
2707 - <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2708 -
2709 - <div class="mxchat-form-group">
2710 - <label for="intent_label">
2711 - <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2712 - </label>
2713 - <input name="intent_label" type="text" id="intent_label" required
2714 - class="mxchat-intent-input"
2715 - 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); ?>">
2716 3794 </div>
2717 -
2718 - <div class="mxchat-form-group">
2719 - <label for="phrases">
2720 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2721 - </label>
2722 - <textarea name="phrases" id="phrases" rows="5" required
2723 - class="mxchat-intent-textarea"
2724 - 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>
2725 - </div>
2726 -
2727 - <div class="mxchat-form-group">
2728 - <label for="callback_function">
2729 - <?php esc_html_e('Callback Function', 'mxchat'); ?>
2730 - </label>
2731 - <select name="callback_function" id="callback_function"
2732 - class="mxchat-intent-select" required>
2733 - <option value="">
2734 - <?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); ?>
2735 3802 </option>
2736 - <?php
2737 - $groups = $this->mxchat_get_available_callbacks(true);
2738 - foreach ($groups as $group_label => $group_callbacks) :
2739 - echo '<optgroup label="' . esc_attr($group_label) . '">';
2740 - foreach ($group_callbacks as $function => $data) :
2741 - $label = $data['label'];
2742 - $pro_only = $data['pro_only'];
2743 - $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2744 - $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2745 - ?>
2746 - <option value="<?php echo esc_attr($function); ?>"
2747 - <?php echo $disabled; ?>>
2748 - <?php echo esc_html($label . $label_suffix); ?>
2749 - </option>
2750 - <?php
2751 - endforeach;
2752 - echo '</optgroup>';
2753 - endforeach;
2754 - ?>
2755 - </select>
2756 - </div>
2757 -
2758 - <button type="submit" class="mxchat-button-primary">
2759 - <?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'); ?>
2760 3807 </button>
2761 3808 </form>
2762 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>
2763 3817
2764 - <!-- Manage Intents Card -->
2765 - <div class="mxchat-card">
2766 - <div class="mxchat-card-header">
2767 - <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2768 - <div class="mxchat-header-actions">
2769 - <form method="get" class="mxchat-search-form">
2770 - <input type="hidden" name="page" value="mxchat-intents">
2771 - <div class="mxchat-search-group">
2772 - <span class="dashicons dashicons-search"></span>
2773 - <input type="text" name="s"
2774 - placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2775 - value="<?php echo esc_attr($search_term); ?>">
2776 - <select name="callback_filter" class="mxchat-intent-filter">
2777 - <option value="">
2778 - <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2779 - </option>
2780 - <?php foreach ($available_callbacks as $function => $callback_data) :
2781 - $label = $callback_data['label']; ?>
2782 - <option value="<?php echo esc_attr($function); ?>"
2783 - <?php selected($callback_filter, $function); ?>>
2784 - <?php echo esc_html($label); ?>
2785 - </option>
2786 - <?php endforeach; ?>
2787 - </select>
2788 - <button type="submit" class="mxchat-button-secondary">
2789 - <?php esc_html_e('Filter', 'mxchat'); ?>
2790 - </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>
2791 3848 </div>
2792 - </form>
2793 - </div>
2794 - </div>
2795 -
2796 - <div class="mxchat-table-wrapper">
2797 - <table class="mxchat-records-table">
2798 - <thead>
2799 - <tr>
2800 - <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2801 - <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2802 - <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2803 - <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2804 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2805 - </tr>
2806 - </thead>
2807 - <tbody>
2808 - <?php if ($intents) :
2809 - foreach ($intents as $intent) :
2810 - $callback_function = $intent->callback_function;
2811 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2812 - ? $available_callbacks[$callback_function]['label']
2813 - : $callback_function;
2814 - $threshold_value = isset($intent->similarity_threshold)
2815 - ? round($intent->similarity_threshold * 100)
2816 - : 85;
2817 -
2818 - // Check if this is a form intent (its intent_label starts with "Form ")
2819 - $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 + }
2820 3868 ?>
2821 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2822 - <td>
2823 - <?php
2824 - if ($is_form_intent) {
2825 - // Attempt to extract the form ID from the intent_label.
2826 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2827 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2828 - if ($form_id) {
2829 - global $wpdb;
2830 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2831 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2832 - if ($form && !empty($form->title)) {
2833 - echo esc_html($form->title);
2834 - } else {
2835 - echo esc_html($intent->intent_label);
2836 - }
2837 - } else {
2838 - echo esc_html($intent->intent_label);
2839 - }
2840 - echo ' <span class="mxchat-badge">Form Intent</span>';
2841 - } else {
2842 - echo esc_html($intent->intent_label);
2843 - }
2844 - ?>
2845 - </td>
2846 - <td class="mxchat-content-cell">
2847 - <?php echo esc_html($intent->phrases); ?>
2848 - </td>
2849 - <td><?php echo esc_html($callback_label); ?></td>
2850 - <td>
2851 - <!-- Similarity threshold adjustment (shown for all intents) -->
2852 - <form method="post"
2853 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2854 - class="mxchat-threshold-form">
2855 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2856 - <input type="hidden" name="action"
2857 - value="mxchat_update_intent_threshold">
2858 - <input type="hidden" name="intent_id"
2859 - value="<?php echo esc_attr($intent->id); ?>">
2860 - <div class="mxchat-slider-group">
2861 - <input type="range"
2862 - name="intent_threshold"
2863 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2864 - min="70"
2865 - max="95"
2866 - value="<?php echo esc_attr($threshold_value); ?>"
2867 - class="mxchat-intent-slider"
2868 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2869 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2870 - class="mxchat-intent-output">
2871 - <?php echo esc_html($threshold_value); ?>%
2872 - </output>
2873 - </div>
2874 - <button type="submit" class="mxchat-button-icon">
2875 - <span class="dashicons dashicons-saved"></span>
2876 - </button>
2877 - </form>
2878 - </td>
2879 - <td class="mxchat-actions-cell">
2880 - <?php if (!$is_form_intent) : ?>
2881 - <!-- Standard intents: allow edit and delete -->
2882 - <button type="button"
2883 - class="mxchat-button-icon mxchat-edit-button"
2884 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2885 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2886 - <span class="dashicons dashicons-edit"></span>
2887 - </button>
2888 - <form method="post"
2889 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2890 - class="mxchat-delete-form"
2891 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2892 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2893 - <input type="hidden" name="action" value="mxchat_delete_intent">
2894 - <input type="hidden" name="intent_id"
2895 - value="<?php echo esc_attr($intent->id); ?>">
2896 - <button type="submit" class="mxchat-button-icon">
2897 - <span class="dashicons dashicons-trash"></span>
2898 - </button>
2899 - </form>
2900 - <?php else : ?>
2901 - <!-- Form intents: show manage in forms button -->
2902 - <?php
2903 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2904 - $form_id = isset($matches[1]) ? $matches[1] : '';
2905 - ?>
2906 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2907 - class="mxchat-button-secondary">
2908 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2909 - </a>
2910 - <?php endif; ?>
2911 - </td>
2912 - </tr>
2913 - <?php endforeach;
2914 - else : ?>
2915 - <tr>
2916 - <td colspan="5" class="mxchat-no-records">
2917 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2918 - </td>
2919 - </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>
2920 3913 <?php endif; ?>
2921 - </tbody>
2922 - </table>
2923 - </div>
2924 -
2925 - <?php if ($total_pages > 1) : ?>
2926 - <div class="mxchat-pagination">
2927 - <?php
2928 - echo paginate_links(array(
2929 - 'base' => add_query_arg('paged', '%#%'),
2930 - 'format' => '',
2931 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
2932 - 'next_text' => __('Next &raquo;', 'mxchat'),
2933 - 'total' => $total_pages,
2934 - 'current' => $page
2935 - ));
2936 - ?>
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>
2937 3928 </div>
2938 3929 <?php endif; ?>
2939 3930 </div>
2940 3931 </div>
2941 -
2942 - <!-- Edit Modal -->
2943 -<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;">
2944 3951 <div class="mxchat-modal-content">
2945 3952 <span class="mxchat-modal-close">&times;</span>
2946 - <div class="mxchat-card-header">
2947 - <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2948 - </div>
2949 3953
2950 - <form id="mxchat-edit-intent-form" method="post"
2951 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2952 - <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2953 - <input type="hidden" name="action" value="mxchat_edit_intent">
2954 - <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="">
2955 3962
2956 - <div class="mxchat-form-group">
2957 - <label for="edit_phrases">
2958 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2959 - </label>
2960 - <textarea name="phrases"
2961 - id="edit_phrases"
2962 - rows="5"
2963 - class="mxchat-intent-textarea"
2964 - 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>
2965 4069 </div>
2966 4070
2967 - <div class="mxchat-modal-actions">
2968 - <button type="submit" class="mxchat-button-primary">
2969 - <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2970 - </button>
2971 - <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2972 - <?php esc_html_e('Cancel', 'mxchat'); ?>
2973 - </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>
2974 4140 </div>
2975 4141 </form>
2976 4142 </div>
2977 4143 </div>
2978 4144
2979 - </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>
2980 4151 </div><!-- .mxchat-wrapper -->
4152 + <?php
4153 +}
2981 4154
2982 - <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
2983 - <div class="mxchat-intent-loading-spinner"></div>
2984 - <div class="mxchat-intent-loading-text">
2985 - <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
2986 - </div>
2987 - </div>
2988 -<?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 . '...';
2989 4172 }
2990 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 +}
2991 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 +}
2992 4236
2993 4237 /**
2994 - * 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
2995 4263 *
2996 4264 * @since 1.0.0
2997 4265 * @return void
2998 4266 */
2999 -public function handle_edit_intent() {
3000 - // Verify nonce and user capabilities
3001 - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
3002 - wp_die(esc_html__('Security check failed.', 'mxchat'));
3003 - }
3004 -
4267 +public function mxchat_handle_edit_intent() {
4268 + // Security checks (nonce and permissions)
3005 4269 if (!current_user_can('manage_options')) {
3006 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
4270 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
3007 4271 }
4272 + check_admin_referer('mxchat_edit_intent');
3008 4273
3009 - // Validate and sanitize input
4274 + // Get POST data
3010 4275 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
3011 - if (!$intent_id) {
3012 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
3013 - }
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
3014 4280
3015 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
3016 - if (empty($phrases_input)) {
3017 - 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;
3018 4285 }
3019 4286
3020 - // Process phrases the same way as in intent creation
3021 4287 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
3022 -
3023 4288 if (empty($phrases_array)) {
3024 - 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;
3025 4291 }
3026 4292
3027 - // Generate embeddings and combine them
4293 + // Generate embeddings with improved error handling
3028 4294 $vectors = [];
4295 + $failed_phrases = [];
4296 +
3029 4297 foreach ($phrases_array as $phrase) {
3030 4298 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3031 4299 if (is_array($embedding_vector)) {
3032 4300 $vectors[] = $embedding_vector;
3033 4301 } else {
3034 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
4302 + $failed_phrases[] = $phrase;
3035 4303 }
3036 4304 }
3037 -
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 +
3038 4316 if (empty($vectors)) {
3039 - 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;
3040 4319 }
3041 4320
3042 - // Create combined vector just like in intent creation
3043 4321 $combined_vector = $this->mxchat_average_vectors($vectors);
3044 4322 $serialized_vector = maybe_serialize($combined_vector);
3045 4323
4324 + // Update the database
3046 4325 global $wpdb;
3047 4326 $table_name = $wpdb->prefix . 'mxchat_intents';
3048 4327
3049 - // Update the intent with both new phrases and the combined vector
3050 4328 $result = $wpdb->update(
3051 4329 $table_name,
3052 4330 array(
4331 + 'intent_label' => $intent_label,
3053 4332 'phrases' => implode(', ', $phrases_array),
3054 - 'embedding_vector' => $serialized_vector
4333 + 'embedding_vector' => $serialized_vector,
4334 + 'similarity_threshold' => $similarity_threshold
3055 4335 ),
3056 4336 array('id' => $intent_id),
3057 - array('%s', '%s'),
3058 - array('%d')
4337 + array('%s', '%s', '%s', '%f'), // Format: string, string, string, float
4338 + array('%d') // Where format: integer
3059 4339 );
3060 4340
3061 4341 if (false === $result) {
3062 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
4342 + $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
4343 + return;
3063 4344 }
3064 4345
3065 - // 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 +
3066 4349 $redirect_url = add_query_arg(
3067 4350 array(
3068 - 'page' => 'mxchat-intents',
3069 - 'updated' => 'true'
4351 + 'page' => 'mxchat-actions'
3070 4352 ),
3071 4353 admin_url('admin.php')
3072 4354 );
3073 -
3074 4355 wp_safe_redirect($redirect_url);
3075 4356 exit;
3076 4357 }
3077 -public function mxchat_handle_update_intent_threshold() {
3078 - if ( ! current_user_can( 'manage_options' ) ) {
3079 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3080 - }
3081 4358
3082 - check_admin_referer('mxchat_update_intent_threshold_nonce');
3083 -
3084 - if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
3085 - global $wpdb;
3086 - $table_name = $wpdb->prefix . 'mxchat_intents';
3087 - $intent_id = intval($_POST['intent_id']);
3088 - $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
3089 - $similarity_threshold = $threshold_percentage / 100;
3090 -
3091 - $wpdb->update(
3092 - $table_name,
3093 - ['similarity_threshold' => $similarity_threshold],
3094 - ['id' => $intent_id],
3095 - ['%f'],
3096 - ['%d']
3097 - );
3098 - }
3099 -
3100 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3101 - exit;
3102 -}
3103 -
4359 +/**
4360 + * Handle adding new intent - with improved error handling
4361 + *
4362 + * @return void
4363 + */
3104 4364 public function mxchat_handle_add_intent() {
3105 - if ( ! current_user_can( 'manage_options' ) ) {
3106 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
4365 + if (!current_user_can('manage_options')) {
4366 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
3107 4367 }
3108 4368
3109 4369 check_admin_referer('mxchat_add_intent_nonce');
3110 4370
@@ -3116,120 +4376,444 @@
3116 4376 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
3117 4377 $default_threshold = 0.85;
3118 4378
3119 4379 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
3120 - 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;
3121 4382 }
3122 4383
3123 4384 $available_callbacks = $this->mxchat_get_available_callbacks();
3124 4385
3125 4386 if (!array_key_exists($callback_function, $available_callbacks)) {
3126 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
4387 + $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
4388 + return;
3127 4389 }
3128 4390
3129 4391 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
3130 4392 if ($is_pro_only && !$this->is_activated) {
3131 - 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;
3132 4395 }
3133 4396
3134 4397 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
3135 4398
3136 4399 if (empty($phrases_array)) {
3137 - 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;
3138 4402 }
3139 4403
4404 + // Generate embeddings with improved error handling
3140 4405 $vectors = [];
4406 + $failed_phrases = [];
4407 +
3141 4408 foreach ($phrases_array as $phrase) {
3142 4409 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3143 4410 if (is_array($embedding_vector)) {
3144 4411 $vectors[] = $embedding_vector;
3145 4412 } else {
3146 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
4413 + $failed_phrases[] = $phrase;
3147 4414 }
3148 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 + }
3149 4426
3150 - if (!empty($vectors)) {
3151 - $combined_vector = $this->mxchat_average_vectors($vectors);
3152 - $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 + }
3153 4431
3154 - $result = $wpdb->insert($table_name, [
3155 - 'intent_label' => $intent_label,
3156 - 'phrases' => implode(', ', $phrases_array),
3157 - 'embedding_vector' => $serialized_vector,
3158 - 'callback_function' => $callback_function,
3159 - 'similarity_threshold' => $default_threshold,
3160 - ]);
4432 + $combined_vector = $this->mxchat_average_vectors($vectors);
4433 + $serialized_vector = maybe_serialize($combined_vector);
3161 4434
3162 - if ($result === false) {
3163 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3164 - }
3165 - } else {
3166 - 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;
3167 4446 }
3168 4447
3169 - 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'));
3170 4452 exit;
3171 4453 }
3172 4454
3173 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 +}
3174 4503
3175 4504
3176 4505
3177 -
3178 -private function mxchat_get_available_callbacks($grouped = false) {
3179 - $callbacks = [
3180 - 'mxchat_handle_email_capture' => [
3181 - 'label' => __('Email Capture', 'mxchat'),
3182 - 'pro_only' => false,
3183 - 'group' => __('Customer Engagement', 'mxchat'),
3184 - ],
3185 - 'mxchat_generate_image' => [
3186 - 'label' => __('Generate Image', 'mxchat'),
3187 - 'pro_only' => true,
3188 - 'group' => __('Other Features', 'mxchat'),
3189 - ],
3190 - 'mxchat_handle_search_request' => [
3191 - 'label' => __('Brave Web Search', 'mxchat'),
3192 - 'pro_only' => false,
3193 - 'group' => __('Search Features', 'mxchat'),
3194 - ],
3195 - 'mxchat_handle_image_search_request' => [
3196 - 'label' => __('Brave Image Search', 'mxchat'),
3197 - 'pro_only' => false,
3198 - 'group' => __('Search Features', 'mxchat'),
3199 - ],
3200 - 'mxchat_handle_pdf_discussion' => [
3201 - 'label' => __('Chat with PDF', 'mxchat'),
3202 - 'pro_only' => true,
3203 - 'group' => __('Other Features', 'mxchat'),
3204 - ],
3205 - 'mxchat_live_agent_handover' => [
3206 - 'label' => __('Live Agent', 'mxchat'),
3207 - 'pro_only' => true,
3208 - 'group' => __('Customer Engagement', 'mxchat'),
3209 - ],
3210 - 'mxchat_handle_switch_to_chatbot_intent' => [
3211 - 'label' => __('Back to Chatbot', 'mxchat'),
3212 - 'pro_only' => true,
3213 - 'group' => __('Customer Engagement', 'mxchat'),
3214 - ],
3215 - ];
3216 -
3217 - // Allow other plugins to add their callbacks
3218 - $callbacks = apply_filters('mxchat_available_callbacks', $callbacks);
3219 -
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 +
3220 4777 // Return grouped structure if requested
3221 4778 if ($grouped) {
3222 - $grouped_callbacks = [];
4779 + $grouped_callbacks = array();
3223 4780 foreach ($callbacks as $key => $data) {
3224 - $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3225 - $grouped_callbacks[$group_label][$key] = [
3226 - 'label' => $data['label'],
3227 - 'pro_only' => $data['pro_only'],
3228 - ];
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;
3229 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 +
3230 4813 return $grouped_callbacks;
3231 4814 }
4815 +
3232 4816 return $callbacks;
3233 4817 }
3234 4818
3235 4819
@@ -3266,9 +4850,9 @@
3266 4850
3267 4851 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3268 4852 }
3269 4853
3270 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
4854 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3271 4855 exit;
3272 4856 }
3273 4857
3274 4858
@@ -3319,41 +4903,98 @@
3319 4903 );
3320 4904
3321 4905
3322 4906 // Existing fields...
3323 - add_settings_field(
3324 - 'api_key',
3325 - esc_html__('OpenAI API Key', 'mxchat'),
3326 - array($this, 'api_key_callback'),
3327 - 'mxchat-chatbot',
3328 - 'mxchat_chatbot_section'
3329 - );
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 + );
3330 4978
3331 4979 add_settings_field(
3332 - 'xai_api_key',
3333 - esc_html__('X.AI API Key', 'mxchat'),
3334 - array($this, 'xai_api_key_callback'),
4980 + 'model',
4981 + esc_html__('Chat Model', 'mxchat'),
4982 + array($this, 'mxchat_model_callback'),
3335 4983 'mxchat-chatbot',
3336 4984 'mxchat_chatbot_section'
3337 4985 );
3338 -
4986 +
4987 + // Add the settings field
3339 4988 add_settings_field(
3340 - 'claude_api_key',
3341 - esc_html__('Claude API Key', 'mxchat'),
3342 - array($this, 'claude_api_key_callback'),
4989 + 'embedding_model',
4990 + esc_html__('Embedding Model', 'mxchat'),
4991 + array($this, 'embedding_model_callback'),
3343 4992 'mxchat-chatbot',
3344 4993 'mxchat_chatbot_section'
3345 4994 );
3346 -
4995 +
3347 4996 add_settings_field(
3348 - 'deepseek_api_key',
3349 - esc_html__('DeepSeek API Key', 'mxchat'),
3350 - array($this, 'deepseek_api_key_callback'),
3351 - 'mxchat-chatbot',
3352 - 'mxchat_chatbot_section'
3353 - );
3354 -
3355 - add_settings_field(
3356 4997 'system_prompt_instructions',
3357 4998 esc_html__('AI Instructions (Behavior)', 'mxchat'),
3358 4999 array($this, 'system_prompt_instructions_callback'),
3359 5000 'mxchat-chatbot',
@@ -3359,24 +5000,25 @@
3359 5000 'mxchat-chatbot',
3360 5001 'mxchat_chatbot_section'
3361 5002 );
3362 5003
5004 +
3363 5005 add_settings_field(
3364 - 'model',
3365 - esc_html__('Model', 'mxchat'),
3366 - array($this, 'mxchat_model_callback'),
5006 + 'top_bar_title',
5007 + esc_html__('Top Bar Title', 'mxchat'),
5008 + array($this, 'mxchat_top_bar_title_callback'),
3367 5009 'mxchat-chatbot',
3368 5010 'mxchat_chatbot_section'
3369 5011 );
3370 5012
3371 5013 add_settings_field(
3372 - 'top_bar_title',
3373 - esc_html__('Top Bar Title', 'mxchat'),
3374 - 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'),
3375 5017 'mxchat-chatbot',
3376 5018 'mxchat_chatbot_section'
3377 5019 );
3378 -
5020 +
3379 5021 add_settings_field(
3380 5022 'enable_email_block',
3381 5023 esc_html__('Require Email To Chat', 'mxchat'),
3382 5024 array($this, 'enable_email_block_callback'),
@@ -3624,8 +5266,26 @@
3624 5266 array($this, 'mxchat_chat_toolbar_toggle_callback'),
3625 5267 'mxchat-embed',
3626 5268 'mxchat_pdf_intent_section'
3627 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 + );
3628 5288
3629 5289 add_settings_field(
3630 5290 'pdf_intent_trigger_text',
3631 5291 __('Intent Trigger Text', 'mxchat'),
@@ -3722,9 +5382,9 @@
3722 5382
3723 5383 // General Settings Section
3724 5384 add_settings_section(
3725 5385 'mxchat_general_section',
3726 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
5386 + esc_html__('YouTube Tutorials', 'mxchat'),
3727 5387 null,
3728 5388 'mxchat-general'
3729 5389 );
3730 5390 }
@@ -3977,95 +5637,92 @@
3977 5637 'mxchat_setting_section_id',
3978 5638 $id === 'model' ? ['label_for' => 'model'] : []
3979 5639 );
3980 5640 }
3981 -
5641 +
5642 +// OpenAI API Key
3982 5643 public function api_key_callback() {
3983 - // Retrieve from your stored 'api_key' in the mxchat_options array
3984 5644 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3985 -
3986 - // Notice we changed the name to "api_key" (no array notation).
3987 - 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" />';
3988 5648 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3989 - echo '<p class="description">' . wp_kses_post(__('The <strong>OpenAI API key is required even when using other models</strong> 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>';
3990 5651 }
5652 +
5653 +// X.AI API Key
3991 5654 public function xai_api_key_callback() {
3992 - // Check if the feature is activated (paid feature)
3993 - $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3994 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3995 -
3996 - // Retrieve the X.AI API key value
3997 5655 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3998 -
3999 - // Render the input field for the X.AI API key
4000 - echo '<div class="' . esc_attr($class) . '">';
4001 - printf(
4002 - '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
4003 - $xaiApiKey,
4004 - $disabled
4005 - );
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" />';
4006 5659 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4007 -
4008 - // If the feature is not activated, show the overlay with a "Pro Only" message
4009 - if (!$this->is_activated) {
4010 - echo '<div class="pro-feature-overlay">';
4011 - 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>';
4012 - echo '</div>';
4013 - }
4014 -
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>';
4015 5661 echo '</div>';
4016 5662 }
5663 +// Claude API Key
4017 5664 public function claude_api_key_callback() {
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 - // Retrieve the Claude API key value directly like the others
4023 5665 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
4024 5666
4025 - // Use direct name field like the other working API keys
4026 - echo '<div class="' . esc_attr($class) . '">';
4027 - printf(
4028 - '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
4029 - $claudeApiKey,
4030 - $disabled
4031 - );
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" />';
4032 5669 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4033 -
4034 - if (!$this->is_activated) {
4035 - echo '<div class="pro-feature-overlay">';
4036 - 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>';
4037 - echo '</div>';
4038 - }
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>';
4039 5671 echo '</div>';
4040 5672 }
5673 +
5674 +// DeepSeek API Key
4041 5675 public function deepseek_api_key_callback() {
4042 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
4043 5676 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4044 -
4045 - // Notice we changed the name to "deepseek_api_key" (no array notation).
4046 - 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" />';
4047 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>';
4048 5683 }
4049 5684
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>';
5693 + echo '</div>';
5694 +}
4050 5695
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>';
5704 + echo '</div>';
5705 +}
4051 5706
4052 -
4053 -
4054 5707 public function mxchat_loops_api_key_callback() {
4055 - // Support both old and new format
4056 5708 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4057 -
4058 - 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">';
4059 5715 echo sprintf(
4060 - '<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" />',
4061 5717 $loops_api_key
4062 5718 );
4063 5719 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4064 5720 echo '</div>';
4065 - 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>';
4066 5722 }
4067 5723
5724 +
4068 5725 public function mxchat_loops_mailing_list_callback() {
4069 5726 // Add error handling and type checking
4070 5727 $loops_api_key = '';
4071 5728 $selected_list = '';
@@ -4164,8 +5821,14 @@
4164 5821
4165 5822 public function mxchat_model_callback() {
4166 5823 // Define available models grouped by provider
4167 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 + ),
4168 5831 esc_html__('X.AI Models', 'mxchat') => array(
4169 5832 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4170 5833 'grok-2' => esc_html__('Grok 2', 'mxchat')
4171 5834 ),
@@ -4172,9 +5835,10 @@
4172 5835 esc_html__('DeepSeek Models', 'mxchat') => array(
4173 5836 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4174 5837 ),
4175 5838 esc_html__('Claude Models', 'mxchat') => array(
4176 - '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'),
4177 5841 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
4178 5842 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4179 5843 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4180 5844 ),
@@ -4185,40 +5849,64 @@
4185 5849 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4186 5850 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4187 5851 )
4188 5852 );
4189 -
5853 +
4190 5854 // Retrieve the currently selected model from saved options
4191 5855 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4192 -
5856 +
4193 5857 // Begin the select dropdown
4194 - echo '<select id="model" name="model">'; // No array notation in name attribute
4195 -
5858 + echo '<select id="model" name="model">';
5859 +
4196 5860 // Iterate over groups of models
4197 5861 foreach ($models as $group_label => $group_models) {
4198 5862 echo '<optgroup label="' . esc_attr($group_label) . '">';
4199 -
5863 +
4200 5864 foreach ($group_models as $model_value => $model_label) {
4201 - // Disable Pro-only models for non-activated users
4202 - $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4203 - $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') : '';
4204 -
4205 - // Output the option element
4206 - 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>';
4207 5867 }
4208 -
5868 +
4209 5869 echo '</optgroup>';
4210 5870 }
4211 -
5871 +
4212 5872 // Close the select dropdown
4213 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 +}
4214 5878
4215 - // Add a description below the dropdown
4216 - 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 +
4217 5906 }
4218 5907
4219 5908
4220 -
4221 5909 public function mxchat_top_bar_title_callback() {
4222 5910 // Retrieve the current value of the top bar title from saved options
4223 5911 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4224 5912
@@ -4227,9 +5915,16 @@
4227 5915
4228 5916 // Add a description
4229 5917 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4230 5918 }
4231 -
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 +}
4232 5927 public function enable_email_block_callback() {
4233 5928 // Load full plugin options array
4234 5929 $all_options = get_option('mxchat_options', []);
4235 5930
@@ -4249,9 +5944,8 @@
4249 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>';
4250 5945 }
4251 5946
4252 5947
4253 -
4254 5948 public function email_blocker_header_content_callback() {
4255 5949 // Load the entire 'mxchat_options' array
4256 5950 $all_options = get_option('mxchat_options', []);
4257 5951
@@ -4259,14 +5953,15 @@
4259 5953 $content = isset($all_options['email_blocker_header_content'])
4260 5954 ? $all_options['email_blocker_header_content']
4261 5955 : '';
4262 5956
4263 - // Render the textarea
5957 + // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4264 5958 echo '<textarea
4265 5959 id="email_blocker_header_content"
4266 5960 name="email_blocker_header_content"
4267 5961 rows="5"
4268 5962 cols="70"
5963 + data-setting="email_blocker_header_content"
4269 5964 >' . esc_textarea($content) . '</textarea>';
4270 5965
4271 5966 echo '<p class="description">';
4272 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');
@@ -4272,9 +5967,8 @@
4272 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');
4273 5968 echo '</p>';
4274 5969 }
4275 5970
4276 -
4277 5971 public function email_blocker_button_text_callback() {
4278 5972 // Load the entire 'mxchat_options' array
4279 5973 $all_options = get_option('mxchat_options', []);
4280 5974
@@ -4295,23 +5989,20 @@
4295 5989
4296 5990 public function mxchat_intro_message_callback() {
4297 5991 // Load the entire 'mxchat_options' array
4298 5992 $all_options = get_option('mxchat_options', []);
4299 -
4300 5993 // Retrieve the saved intro message or use the default
4301 5994 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4302 5995 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4303 -
4304 - // Output the textarea with the saved value
5996 + // Output the textarea with the saved value without escaping HTML
4305 5997 ?>
4306 - <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>
4307 5999 <p class="description">
4308 - <?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'); ?>
4309 6001 </p>
4310 6002 <?php
4311 6003 }
4312 6004
4313 -
4314 6005 public function mxchat_input_copy_callback() {
4315 6006 // Load the entire 'mxchat_options' array
4316 6007 $all_options = get_option('mxchat_options', []);
4317 6008
@@ -5108,9 +6799,78 @@
5108 6799 echo '</div>';
5109 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>';
5110 6801 }
5111 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 +}
5112 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 +
5113 6873 public function mxchat_pdf_intent_trigger_text_callback() {
5114 6874 $disabled = $this->is_activated ? '' : 'disabled';
5115 6875 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5116 6876 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
@@ -5331,24 +7091,21 @@
5331 7091 }
5332 7092 echo '</div>';
5333 7093 }
5334 7094
5335 -
5336 7095 public function mxchat_similarity_threshold_callback() {
5337 7096 // Load from mxchat_options array
5338 7097 $options = get_option('mxchat_options', []);
5339 -
5340 - // Get value with backwards compatibility
5341 - $threshold = isset($options['similarity_threshold'])
5342 - ? $options['similarity_threshold']
5343 - : get_option('mxchat_similarity_threshold', 80);
5344 -
7098 +
7099 + // Get value from options array with default of 80
7100 + $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
7101 +
5345 7102 echo '<div class="slider-container">';
5346 7103 echo sprintf(
5347 7104 '<input type="range"
5348 7105 id="similarity_threshold"
5349 7106 name="similarity_threshold"
5350 - min="70"
7107 + min="20"
5351 7108 max="85"
5352 7109 step="1"
5353 7110 value="%s"
5354 7111 class="range-slider" />',
@@ -5358,16 +7115,14 @@
5358 7115 '<span id="threshold_value" class="range-value">%s</span>',
5359 7116 esc_html($threshold)
5360 7117 );
5361 7118 echo '</div>';
5362 -
5363 7119 echo '<p class="description">';
5364 - 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');
5365 7121 echo '</p>';
5366 7122 }
5367 7123
5368 7124
5369 -
5370 7125 public function mxchat_live_agent_secret_key_callback() {
5371 7126 $disabled = $this->is_activated ? '' : 'disabled';
5372 7127 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5373 7128
@@ -5412,9 +7167,9 @@
5412 7167 public function mxchat_enqueue_admin_assets() {
5413 7168 wp_enqueue_style('wp-color-picker');
5414 7169
5415 7170 // Get the plugin version or file modification time for cache busting
5416 - $plugin_version = '2.0.5'; // Replace this with your plugin's version
7171 + $plugin_version = '2.1.5'; // Replace this with your plugin's version
5417 7172
5418 7173 // File paths
5419 7174 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5420 7175 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
@@ -5420,8 +7175,10 @@
5420 7175 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5421 7176 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5422 7177 $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5423 7178 $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
7179 + $transcripts_css_path = plugin_dir_path(__FILE__) . '../css/chat-transcripts.css';
7180 + $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
5424 7181
5425 7182 // Check if files exist and get modification times
5426 7183 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5427 7184 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
@@ -5427,9 +7184,38 @@
5427 7184 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5428 7185 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5429 7186 $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5430 7187 $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5431 -
7188 + $transcripts_css_version = file_exists($transcripts_css_path) ? filemtime($transcripts_css_path) : $plugin_version;
7189 + $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
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 +
5432 7218 // Enqueue scripts and styles with corrected paths
5433 7219 wp_enqueue_script(
5434 7220 'mxchat-color-picker',
5435 7221 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
@@ -5453,15 +7239,23 @@
5453 7239 $plugin_version,
5454 7240 true
5455 7241 );
5456 7242
5457 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
5458 - 'ajax_url' => admin_url('admin-ajax.php'),
5459 - 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5460 - 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5461 - 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5462 - 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
5463 - ));
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 +));
5464 7258
5465 7259 // Enqueue the admin CSS
5466 7260 wp_enqueue_style(
5467 7261 'mxchat-admin-css',
@@ -5469,9 +7263,10 @@
5469 7263 array(),
5470 7264 $admin_css_version
5471 7265 );
5472 7266
5473 - if (isset($_GET['page']) && $_GET['page'] === 'mxchat-transcripts') {
7267 + // Conditional enqueue for transcripts page
7268 + if ($current_page === 'mxchat-transcripts') {
5474 7269 wp_enqueue_style(
5475 7270 'mxchat-chat-transcripts-css',
5476 7271 plugin_dir_url(__FILE__) . '../css/chat-transcripts.css',
5477 7272 array(),
@@ -5486,28 +7281,33 @@
5486 7281 true
5487 7282 );
5488 7283 }
5489 7284
5490 - wp_enqueue_style(
5491 - 'mxchat-knowledge-css',
5492 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5493 - array(),
5494 - $knowledge_css_version
5495 - );
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 + }
5496 7294
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 + }
5497 7304
5498 - wp_enqueue_style(
5499 - 'mxchat-intent-css',
5500 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5501 - array(),
5502 - $intent_css_version
5503 - );
5504 -
5505 7305 // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5506 - wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5507 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
5508 - 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5509 - ) );
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 + ));
5510 7310
5511 7311 // Localize the script for color picker and settings
5512 7312 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5513 7313 'ajax_url' => admin_url('admin-ajax.php'),
@@ -5537,8 +7337,10 @@
5537 7337 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5538 7338 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5539 7339 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5540 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',
5541 7343 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5542 7344 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5543 7345 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5544 7346 ));
@@ -5553,9 +7355,9 @@
5553 7355 }
5554 7356
5555 7357 if (isset($input['similarity_threshold'])) {
5556 7358 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5557 - $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
5558 7360 }
5559 7361
5560 7362 if (isset($input['xai_api_key'])) {
5561 7363 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
@@ -5567,8 +7369,12 @@
5567 7369
5568 7370 if (isset($input['deepseek_api_key'])) {
5569 7371 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5570 7372 }
7373 +
7374 + if (isset($input['gemini_api_key'])) {
7375 + $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
7376 + }
5571 7377
5572 7378 if (isset($input['enable_woocommerce_integration'])) {
5573 7379 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5574 7380 }
@@ -5605,8 +7411,12 @@
5605 7411
5606 7412 if (isset($input['top_bar_title'])) {
5607 7413 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5608 7414 }
7415 +
7416 + if (isset($input['ai_agent_text'])) {
7417 + $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
7418 + }
5609 7419
5610 7420 if (isset($input['enable_email_block'])) {
5611 7421 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5612 7422 }
@@ -5619,9 +7429,9 @@
5619 7429 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5620 7430 }
5621 7431
5622 7432 if (isset($input['intro_message'])) {
5623 - $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
5624 7434 }
5625 7435
5626 7436 if (isset($input['input_copy'])) {
5627 7437 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
@@ -5659,14 +7469,36 @@
5659 7469
5660 7470 if (isset($input['pre_chat_message'])) {
5661 7471 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5662 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 + }
5663 7490
5664 - if (isset($input['model'])) {
7491 + if (isset($input['model'])) {
5665 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',
5666 7497 'grok-beta',
5667 7498 'grok-2',
5668 7499 'deepseek-chat',
7500 + 'claude-3-7-sonnet-20250219',
5669 7501 'claude-3-5-sonnet-20241022',
5670 7502 'claude-3-opus-20240229',
5671 7503 'claude-3-sonnet-20240229',
5672 7504 'claude-3-haiku-20240307',
@@ -5835,8 +7667,22 @@
5835 7667
5836 7668 if (isset($input['chat_toolbar_toggle'])) {
5837 7669 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5838 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 + }
5839 7685
5840 7686 if (isset($input['pdf_intent_trigger_text'])) {
5841 7687 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5842 7688 }
@@ -5891,62 +7737,10 @@
5891 7737 }
5892 7738
5893 7739
5894 7740
5895 -private static function mxchat_extract_main_content($html) {
5896 - if (empty($html)) {
5897 - //error_log('mxchat_extract_main_content: Empty HTML content received');
5898 - return '';
5899 - }
5900 7741
5901 - try {
5902 - $dom = new DOMDocument;
5903 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
5904 7742
5905 - // Simple load of HTML, with @ to suppress warnings
5906 - @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
5907 -
5908 - $xpath = new DOMXPath($dom);
5909 -
5910 - // Simplified selectors focusing on common content areas
5911 - $selectors = [
5912 - '//article',
5913 - '//*[@id="content"]',
5914 - '//*[@class="entry-content"]',
5915 - '//main'
5916 - ];
5917 -
5918 - foreach ($selectors as $selector) {
5919 - $nodes = $xpath->query($selector);
5920 - if ($nodes && $nodes->length > 0) {
5921 - $content = '';
5922 - foreach ($nodes as $node) {
5923 - $content .= $dom->saveHTML($node);
5924 - }
5925 - if (!empty($content)) {
5926 - return $content;
5927 - }
5928 - }
5929 - }
5930 -
5931 - // Fallback: Return the entire body content if no specific selector matches
5932 - $body = $dom->getElementsByTagName('body');
5933 - if ($body->length > 0) {
5934 - return $dom->saveHTML($body->item(0));
5935 - }
5936 -
5937 - // Last resort: return the original HTML
5938 - return $html;
5939 -
5940 - } catch (Exception $e) {
5941 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
5942 - return $html; // Return original HTML if parsing fails
5943 - } finally {
5944 - libxml_clear_errors();
5945 - }
5946 -}
5947 -
5948 -
5949 7743 private function mxchat_fetch_loops_mailing_lists($api_key) {
5950 7744 $url = 'https://app.loops.so/api/v1/lists';
5951 7745 $response = wp_remote_get($url, array(
5952 7746 'headers' => array(
@@ -5991,8 +7785,81 @@
5991 7785 }
5992 7786 }
5993 7787
5994 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 +}
5995 7862
5996 7863
5997 7864 }
5998 7865 ?>