PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.2
MxChat – AI Chatbot & Content Generation for WordPress v2.0.2
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
mxchat-basic / includes / class-mxchat-admin.php

class-mxchat-admin.php in MxChat – AI Chatbot & Content Generation for WordPress 2.0.2, at includes/class-mxchat-admin.php

5,981 lines 252.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Admin {
7 private $options;
8 private $chat_count;
9 private $is_activated;
10
11 public function __construct() {
12 $this->options = get_option('mxchat_options');
13 $this->chat_count = get_option('mxchat_chat_count', 0);
14 $this->is_activated = $this->is_license_active();
15
16 // Initialize default options if they are not set
17 if (!$this->options) {
18 $this->initialize_default_options();
19 }
20
21 // Add admin menu and initialize settings
22 add_action('admin_menu', array($this, 'mxchat_add_plugin_page'));
23 add_action('admin_init', array($this, 'mxchat_page_init'));
24 add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
25 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
27 add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
28 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
36 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
37 add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
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'));
42 add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
43 add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
44 add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
49 add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
50 if (isset($this->options['enable_woocommerce_integration']) &&
51 ($this->options['enable_woocommerce_integration'] === '1' ||
52 $this->options['enable_woocommerce_integration'] === 'on')) {
53
54 //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
55 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
56 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
57 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
58 }
59 }
60
61 // Method to check if the license is active
62 private function is_license_active() {
63 $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
64 return $license_status === esc_html__('active', 'mxchat');
65 }
66
67 // Initialize default options
68 private function initialize_default_options() {
69 $default_options = array(
70 'api_key' => '',
71 'xai_api_key' => '',
72 'claude_api_key' => '',
73 'deepseek_api_key' => '',
74 'system_prompt_instructions' => esc_html__('[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind:
75 - Your name is [Chatbot Name].
76 - Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject.
77 - Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs.
78 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
79 - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'),
80 'model' => esc_html__('gpt-4o', 'mxchat'),
81 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
82 'role_rate_limits' => array(),
83 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
84 'enable_email_block' => '',
85 'email_blocker_header_content' => esc_html__("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
86 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
87 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
88 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
89 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
90 'append_to_body' => esc_html__('off', 'mxchat'),
91 'close_button_color' => esc_html__('#fff', 'mxchat'),
92 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
93 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
94 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
95 'bot_message_bg_color' => esc_html__('#212121', 'mxchat'),
96 'bot_message_font_color' => esc_html__('#fff', 'mxchat'),
97 'top_bar_bg_color' => esc_html__('#212121', 'mxchat'),
98 'send_button_font_color' => esc_html__('#212121', 'mxchat'),
99 'chat_input_font_color' => esc_html__('#212121', 'mxchat'),
100 'chatbot_background_color' => esc_html__('#212121', 'mxchat'),
101 'icon_color' => esc_html__('#fff', 'mxchat'),
102 'enable_woocommerce_integration' => esc_html__('0', 'mxchat'),
103 'link_target_toggle' => esc_html__('off', 'mxchat'),
104 'pre_chat_message' => esc_html__('Hey there! Ask me anything!', 'mxchat'),
105
106 // New fields for Loops Integration
107 'loops_api_key' => '',
108 'loops_mailing_list' => '',
109 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
110 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
111 'popular_question_1' => '',
112 'popular_question_2' => '',
113 'popular_question_3' => '',
114 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
115 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
116 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
117 'pdf_max_pages' => 69,
118
119 // Live Agent Integration
120 'live_agent_webhook_url' => '',
121 'live_agent_secret_key' => '',
122 'live_agent_bot_token' => '',
123 'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'),
124 'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'),
125 'chat_toolbar_toggle' => esc_html__('off', 'mxchat'),
126 'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'),
127 'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'),
128 'toolbar_icon_color' => esc_html__('#212121', 'mxchat'),
129 );
130
131
132 // Merge existing options with defaults
133 $existing_options = get_option('mxchat_options', array());
134 $merged_options = wp_parse_args($existing_options, $default_options);
135
136 // Update the options if they have changed
137 if ($existing_options !== $merged_options) {
138 update_option('mxchat_options', $merged_options);
139 }
140
141 // Add default limits for each role
142 $roles = wp_roles()->get_names();
143 foreach ($roles as $role_id => $role_name) {
144 $default_options['role_rate_limits'][$role_id] = esc_html__('100', 'mxchat');
145 }
146
147 return $default_options;
148
149 // Update the $this->options property
150 $this->options = $merged_options;
151 }
152
153
154
155 public function mxchat_add_plugin_page() {
156 // Main menu page
157 add_menu_page(
158 esc_html__('MxChat Settings', 'mxchat'),
159 esc_html__('MxChat', 'mxchat'),
160 'manage_options',
161 'mxchat-max',
162 array($this, 'mxchat_create_admin_page'),
163 'dashicons-testimonial',
164 6
165 );
166
167 // Submenu page for Knowledge
168 add_submenu_page(
169 'mxchat-max',
170 esc_html__('Prompts', 'mxchat'),
171 esc_html__('Knowledge', 'mxchat'),
172 'manage_options',
173 'mxchat-prompts',
174 array($this, 'mxchat_create_prompts_page')
175 );
176
177 // Submenu page for Chat Transcripts
178 add_submenu_page(
179 'mxchat-max', // Corrected parent slug to match the main menu
180 esc_html__('Chat Transcripts', 'mxchat'),
181 esc_html__('Transcripts', 'mxchat'),
182 'manage_options',
183 'mxchat-transcripts',
184 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
185 );
186
187 // Submenu page for Intents
188 add_submenu_page(
189 'mxchat-max', // Parent slug
190 esc_html__('MxChat Intents', 'mxchat'), // Page title
191 esc_html__('Intents', 'mxchat'), // Menu title
192 'manage_options', // Capability
193 'mxchat-intents', // Menu slug
194 array($this, 'mxchat_intents_page_html') // Callback function
195 );
196
197 add_submenu_page(
198 'mxchat-max',
199 esc_html__('Add Ons', 'mxchat'),
200 esc_html__('Add Ons', 'mxchat'),
201 'manage_options',
202 'mxchat-addons',
203 array($this, 'mxchat_create_addons_page')
204 );
205
206 // Submenu page for Activation Key
207 add_submenu_page(
208 'mxchat-max',
209 esc_html__('Pro Upgrade', 'mxchat'),
210 esc_html__('Pro Upgrade', 'mxchat'),
211 'manage_options',
212 'mxchat-activation',
213 array($this, 'mxchat_create_activation_page')
214 );
215 }
216
217 public function mxchat_create_addons_page() {
218 require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php';
219 $addons_page = new MxChat_Addons();
220 $addons_page->render_page();
221 }
222
223 public function mxchat_save_setting_callback() {
224 check_ajax_referer('mxchat_save_setting_nonce');
225 if (!current_user_can('manage_options')) {
226 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
227 }
228 $name = isset($_POST['name']) ? $_POST['name'] : '';
229 // Strip slashes from the value before saving
230 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
231 if (empty($name)) {
232 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
233 }
234 // Load the full options array
235 $options = get_option('mxchat_options', []);
236 // Handle special cases
237 switch ($name) {
238 case 'additional_popular_questions':
239 $questions = json_decode($value, true); // No need for stripslashes here
240 if (is_array($questions)) {
241 $options[$name] = $questions;
242 // Also update old option for backwards compatibility
243 update_option('additional_popular_questions', $questions);
244 }
245 break;
246 case 'user_message_bg_color':
247 case 'user_message_font_color':
248 case 'bot_message_bg_color':
249 case 'bot_message_font_color':
250 case 'top_bar_bg_color':
251 case 'send_button_font_color':
252 case 'chatbot_background_color':
253 case 'icon_color':
254 case 'chat_input_font_color':
255 case 'live_agent_message_bg_color':
256 case 'live_agent_message_font_color':
257 case 'mode_indicator_bg_color':
258 case 'mode_indicator_font_color':
259 case 'toolbar_icon_color':
260 // Store color values directly
261 $options[$name] = $value;
262 break;
263 case 'live_agent_status':
264 // Set the new value
265 $options[$name] = ($value === 'on') ? 'on' : 'off';
266 break;
267 case 'enable_woocommerce_integration':
268 // Handle values that used to be 1/0
269 $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
270 break;
271 default:
272 // Handle role rate limits
273 if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
274 // Extract role ID from the name
275 preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
276 if (isset($matches[1])) {
277 $role_id = $matches[1];
278 // Initialize role_rate_limits if it doesn't exist
279 if (!isset($options['role_rate_limits'])) {
280 $options['role_rate_limits'] = [];
281 }
282 // Update the specific role's rate limit
283 $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
284 break;
285 }
286 }
287 // Handle toggles
288 if (strpos($name, 'toggle') !== false || in_array($name, [
289 'chat_persistence_toggle',
290 'privacy_toggle',
291 'complianz_toggle',
292 'chat_toolbar_toggle' // Removed live_agent_status from here
293 ])) {
294 $options[$name] = ($value === 'on') ? 'on' : 'off';
295 } else {
296 // Store all other values directly
297 $options[$name] = $value;
298 }
299 break;
300 }
301 // Save all updates
302 $updated = update_option('mxchat_options', $options);
303 // Handle backwards compatibility for certain fields
304 $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
305 'brave_country', 'brave_language', 'similarity_threshold'];
306 if (in_array($name, $legacy_fields)) {
307 // Also update the individual option for backwards compatibility
308 update_option($name, $value);
309 }
310 if ($updated) {
311 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
312 } else {
313 wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
314 }
315 }
316
317 /**
318 * Handles AJAX auto-save for prompts and auto-sync settings.
319 */
320 public function mxchat_save_prompts_setting_callback() {
321 // Check the correct nonce for this action.
322 check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
323
324 if ( ! current_user_can( 'manage_options' ) ) {
325 wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
326 }
327
328 $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
329 $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
330
331 if ( empty( $name ) ) {
332 wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
333 }
334
335 // Handle standalone auto-sync settings.
336 if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
337 // Convert checkbox value to "1" if checked, "0" if not.
338 $value = ( $value === 'on' ) ? '1' : '0';
339 update_option( $name, $value );
340 wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
341 }
342
343 // Handle fields stored in the 'mxchat_prompts_options' array.
344 // Handle fields stored in the 'mxchat_prompts_options' array.
345 if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
346 // Extract the key name using regex.
347 if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
348 $key = sanitize_text_field( $matches[1] );
349 $options = get_option( 'mxchat_prompts_options', [] );
350
351 // Remove Pinecone-specific settings from the main plugin handling.
352 if ( 'mxchat_use_pinecone' === $key ) {
353 // Optionally, you could simply ignore this field or set a default value.
354 // For example, ensure it's always 0 or remove it entirely:
355 // $options[ $key ] = '0';
356 // Or, if you prefer, just return an error so that the main plugin doesn't handle it:
357 wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
358 } else {
359 // For all other fields, use the existing logic.
360 $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
361 }
362 update_option( 'mxchat_prompts_options', $options );
363 wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
364 }
365 }
366
367
368 wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
369 }
370
371 public function mxchat_display_admin_notice() {
372 // Success notice
373 if ($message = get_transient('mxchat_admin_notice_success')) {
374 ?>
375 <div class="notice notice-success is-dismissible">
376 <p><?php echo esc_html($message); ?></p>
377 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
378 </div>
379 <?php
380 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
381 }
382
383 // Error notice
384 if ($message = get_transient('mxchat_admin_notice_error')) {
385 ?>
386 <div class="notice notice-error is-dismissible">
387 <p><?php echo esc_html($message); ?></p>
388 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
389 </div>
390 <?php
391 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
392 }
393 }
394
395
396
397
398
399 public function mxchat_create_admin_page() {
400
401 ?>
402 <div class="wrap mxchat-admin">
403 <?php if (!$this->is_activated): ?>
404 <div class="mxchat-pro-banner">
405 <p>
406 <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
407 <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
408 </p>
409 </div>
410 <?php endif; ?>
411
412 <div class="mxchat-agents-banner">
413 <p>
414 <?php echo esc_html__('Pro users get exclusive access to', 'mxchat'); ?> <a href="https://mxchat.ai/agents/" target="_blank"><?php echo esc_html__('MxChat AI Agents', 'mxchat'); ?></a> <?php echo esc_html__('– our groundbreaking platform to test, refine, and deploy your chatbot with confidence. Take your AI assistant to the next level!', 'mxchat'); ?>
415 </p>
416 </div>
417
418 <h2 class="mxchat-nav-tab-wrapper">
419 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
420 <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
421 <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
422 </h2>
423
424 <div id="chatbot" class="mxchat-tab-content active">
425 <div class="mxchat-autosave-section">
426 <?php do_settings_sections('mxchat-chatbot'); ?>
427 </div>
428 </div>
429
430 <div id="embed" class="mxchat-tab-content">
431 <div class="mxchat-autosave-section">
432 <div class="mxchat-settings-section">
433 <h2><?php echo esc_html__('WooCommerce Settings', 'mxchat'); ?></h2>
434 <table class="form-table">
435 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
436 </table>
437 </div>
438 <div class="section-divider"></div>
439
440 <div class="mxchat-settings-section">
441 <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
442 <table class="form-table">
443 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
444 </table>
445 </div>
446
447 <div class="section-divider"></div>
448
449 <div class="mxchat-settings-section">
450 <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
451 <table class="form-table">
452 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
453 </table>
454 </div>
455
456 <div class="section-divider"></div>
457
458 <div class="mxchat-settings-section">
459 <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
460 <table class="form-table">
461 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
462 </table>
463 </div>
464
465 <div class="section-divider"></div>
466
467 <!-- Live Agent Settings Section -->
468 <div class="mxchat-settings-section">
469 <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
470 <p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('to set up live agent transfer via Slack.', 'mxchat'); ?></p>
471 <table class="form-table">
472 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
473 </table>
474 </div>
475 </div>
476 </div>
477
478
479 <div id="general" class="mxchat-tab-content">
480 <?php do_settings_sections('mxchat-general'); ?>
481 <p>
482 <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
483 <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
484 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
485 </p>
486 <p>
487 <?php echo esc_html__('If you like our plugin, please consider', 'mxchat'); ?> <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('leaving us a review', 'mxchat'); ?></a>.
488 </p>
489
490 <div class="faq-item">
491 <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
492 <p>
493 <?php echo esc_html__('The Claude API, provided by Anthropic, allows for intelligent, context-aware chatbot responses in MxChat. To use it, you will need both an OpenAI API key and a Claude API key. This is necessary because the system utilizes OpenAI for vector embedding in the Retrieval-Augmented Generation (RAG) process, as Claude does not currently offer an embedding API.', 'mxchat'); ?>
494 </p>
495 <p>
496 <?php echo esc_html__('When using the Claude API, your custom content is sent to Claude for generating responses, while the embeddings are processed through OpenAI. This ensures your chatbot provides accurate and engaging responses while maintaining knowledge relevant to your website.', 'mxchat'); ?>
497 </p>
498 <p>
499 <?php echo esc_html__('You can obtain your Claude API key by signing up on the', 'mxchat'); ?> <a href="https://www.anthropic.com/api" target="_blank" rel="noopener"><?php echo esc_html__('Anthropic API page', 'mxchat'); ?></a>.
500 </p>
501 </div>
502
503 <div class="faq-item">
504 <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
505 <p>
506 <?php echo esc_html__('The X.AI API, released on 10.21.24, is currently in beta. To use it, you will need both an OpenAI API key and an X.AI API key. This is because OpenAI handles vector embeddings in the Retrieval-Augmented Generation (RAG) process, as X.AI does not yet provide an embedding API.', 'mxchat'); ?>
507 </p>
508 <p>
509 <?php echo esc_html__('Custom content is sent to the X.AI API for generating responses, while OpenAI processes the embeddings. This allows the chatbot to provide advanced responses while maintaining context from your website. You can get your X.AI API key from the', 'mxchat'); ?> <a href="https://docs.x.ai/docs" target="_blank" rel="noopener"><?php echo esc_html__('X.AI API documentation', 'mxchat'); ?></a>.
510 </p>
511 </div>
512
513 <div class="faq-item">
514 <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
515 <p>
516 <?php echo esc_html__('Yes, you will need an OpenAI API key to power the chatbot. You can obtain an API key by signing up on the', 'mxchat'); ?> <a href="https://platform.openai.com/signup" target="_blank" rel="noopener"><?php echo esc_html__('OpenAI platform', 'mxchat'); ?></a>. <?php echo esc_html__('After signing up, you must add credits to your account—typically, $5 in credits is sufficient to get started.', 'mxchat'); ?>
517 </p>
518 <p>
519 <?php echo esc_html__('Once you have your API key, simply enter it in the chatbot\'s settings to enable functionality. The chatbot relies on OpenAI’s models for generating responses, so having sufficient credits in your OpenAI account is essential for smooth operation.', 'mxchat'); ?>
520 </p>
521 </div>
522
523 <div class="faq-item">
524 <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
525 <p>
526 <?php echo esc_html__('You can add the chatbot using the shortcode', 'mxchat'); ?> <code>[mxchat_chatbot floating="yes"]</code> <?php echo esc_html__('or', 'mxchat'); ?> <code>[mxchat_chatbot floating="no"]</code>. <?php echo esc_html__('For initial testing and styling, it\'s best to use the shortcode on a draft or non-public page. Once you’re ready to go live, enable the “Append Chat Widget to Body” option in the settings for site-wide integration (recommended) or add shortcode to the footer.', 'mxchat'); ?>
527 </p>
528 </div>
529
530 <div class="faq-item">
531 <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
532 <p>
533 <?php echo esc_html__('The chatbot uses AI and vector embeddings to connect users with relevant information. When you submit content, it converts it into a mathematical format. When a user asks a question, the bot matches it to your stored content and generates a response based on relevance. For example, submitting "Our phone number is 910-123-4567" allows the bot to retrieve this information when asked about contact details. To ensure related information is provided together, submit it in one entry.', 'mxchat'); ?>
534 </p>
535 </div>
536
537 <div class="faq-item">
538 <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
539 <p>
540 <?php echo esc_html__('Occasionally, the chatbot may generate inaccurate links or information, known as "hallucinations." To minimize this, you can add system instructions to guide its behavior. For example, include an instruction like: "Only provide links you directly have access to or retrieve from the knowledge base. Do not make up links or information that you do not have direct access to." This helps the bot stay aligned with your content.', 'mxchat'); ?>
541 </p>
542 </div>
543
544 <div class="faq-item">
545 <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
546 <p>
547 <?php echo esc_html__('Intents allow MxChat to recognize user requests and trigger specific actions, such as capturing emails or displaying product information. This helps provide a more interactive and responsive experience. For a detailed explanation of each intent, please refer to our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Intents Documentation', 'mxchat'); ?></a>.
548 </p>
549 </div>
550
551 <div class="faq-item">
552 <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
553 <p>
554 <?php echo esc_html__('The Pro version offers direct integration with the Complianz GDPR plugin, making it easy to stay compliant with GDPR. If Complianz is enabled on your website, users must accept consent before the chatbot widget appears. The chatbot will only display once the user has accepted, ensuring compliance with data privacy regulations.', 'mxchat'); ?>
555 </p>
556 </div>
557
558 <div class="faq-item">
559 <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
560 <p>
561 <?php echo esc_html__('With WooCommerce integration, the chatbot automatically embeds new products and updates existing ones. For already-published products, you can click update or submit the product sitemap. The “Order History Access” setting allows the bot to access users\' order history (requires login) and assist with order inquiries. To enable the “Add to Cart” feature, add this system instruction: “After discussing a product, ask if the user wants to add it to their cart. The user must say \'Add to cart\' exactly.” Currently, this feature supports English only, with more languages coming soon.', 'mxchat'); ?>
562 </p>
563 </div>
564
565 <div class="faq-item">
566 <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
567 <p>
568 <?php echo esc_html__('AI chatbots can sometimes behave in unexpected ways, especially if you\'re new to configuring AI for specific tasks. We\'re committed to helping you get the most out of your chatbot experience. While we’re working on comprehensive guides and video tutorials, our team is here to assist you directly. If your chatbot isn\'t delivering the responses you need, please don’t hesitate to', 'mxchat'); ?> <a href="https://mxchat.ai/contact/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('contact us', 'mxchat'); ?></a> <?php echo esc_html__('for personalized support.', 'mxchat'); ?>
569 </p>
570 <p>
571 <?php echo esc_html__('We’re dedicated to your success and ready to guide you in aligning the AI\'s behavior to meet your goals.', 'mxchat'); ?>
572 </p>
573 </div>
574
575 <div class="faq-item">
576 <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
577 <p>
578 <?php echo esc_html__('Loops is a powerful SaaS email service that helps you automate and enhance your email marketing campaigns, making it easy to reach and engage with your audience. To integrate Loops with MxChat, you’ll need an API key from Loops. You can obtain this key by logging into your Loops account and navigating to the API settings. Visit the', 'mxchat'); ?> <a href="https://loops.so" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Loops website', 'mxchat'); ?></a> <?php echo esc_html__('to get started or to sign up for an account.', 'mxchat'); ?>
579 </p>
580 </div>
581
582 </div>
583 </div>
584 <?php
585 }
586
587
588
589 public function mxchat_create_transcripts_page() {
590 global $wpdb;
591 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
592
593 // Get basic stats
594 $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
595 $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
596 $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
597 ?>
598 <div class="wrap mxchat-admin">
599 <!-- Stats Cards -->
600 <div class="mxchat-stats-grid">
601 <div class="mxchat-stat-card">
602 <div class="stat-icon">💬</div>
603 <div class="stat-content">
604 <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
605 <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
606 </div>
607 </div>
608 <div class="mxchat-stat-card">
609 <div class="stat-icon">📝</div>
610 <div class="stat-content">
611 <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
612 <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
613 </div>
614 </div>
615 <div class="mxchat-stat-card">
616 <div class="stat-icon">👥</div>
617 <div class="stat-content">
618 <span class="stat-value"><?php echo esc_html($total_users); ?></span>
619 <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
620 </div>
621 </div>
622 </div>
623
624 <!-- Search and Filter Controls -->
625 <div class="mxchat-controls-wrapper">
626 <div class="mxchat-search-box">
627 <input type="text" id="mxchat-search-transcripts"
628 placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
629 class="regular-text">
630 </div>
631
632 <form id="mxchat-delete-form" method="post">
633 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
634 <div class="mxchat-controls">
635 <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
636 <span class="dashicons dashicons-yes-alt"></span>
637 <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
638 </button>
639 <button type="submit" class="button delete-chats-button">
640 <span class="dashicons dashicons-trash"></span>
641 <?php esc_html_e('Delete Selected', 'mxchat'); ?>
642 </button>
643 </div>
644 </form>
645 </div>
646
647 <!-- Transcripts Container -->
648 <div id="mxchat-transcripts"></div>
649 </div>
650 <?php
651 }
652
653 public function mxchat_create_prompts_page() {
654 global $wpdb;
655 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
656
657 // Display success message if all prompts were deleted
658 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
659 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
660 }
661
662 // Set up pagination and search query
663 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
664 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
665 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
666 $per_page = 10;
667 $offset = ($current_page - 1) * $per_page;
668
669 // Modify query to handle search input
670 $sql_search = "";
671 if ($search_query) {
672 $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
673 }
674
675 // Retrieve total number of prompts, considering search filter
676 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
677 $total_pages = ceil($total_prompts / $per_page);
678
679 // Retrieve prompts from the database
680 $prompts = $wpdb->get_results(
681 $wpdb->prepare(
682 "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
683 $per_page,
684 $offset
685 )
686 );
687
688 // Add the pagination links generation here
689 $page_links = paginate_links(array(
690 'base' => add_query_arg(array(
691 'paged' => '%#%',
692 'search' => urlencode($search_query),
693 '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
694 ), admin_url('admin.php?page=mxchat-prompts')),
695 'format' => '',
696 'prev_text' => __('&laquo; Previous', 'mxchat'),
697 'next_text' => __('Next &raquo;', 'mxchat'),
698 'total' => $total_pages,
699 'current' => $current_page,
700 ));
701
702 // Retrieve processing statuses
703 $pdf_url = get_transient('mxchat_last_pdf_url');
704 $sitemap_url = get_transient('mxchat_last_sitemap_url');
705 $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
706 $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
707
708 if ($pdf_status && $pdf_status['status'] === 'complete') {
709 delete_transient('mxchat_last_pdf_url');
710 $pdf_status = false;
711 }
712 if ($sitemap_status && $sitemap_status['status'] === 'complete') {
713 delete_transient('mxchat_last_sitemap_url');
714 $sitemap_status = false;
715 }
716
717 $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
718 || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
719 ?>
720 <div class="wrap mxchat-wrapper">
721 <!-- Hero Section -->
722 <div class="mxchat-hero">
723 <h1 class="mxchat-main-title">
724 <span class="mxchat-gradient-text">Knowledge Base</span> Manager
725 </h1>
726 <p class="mxchat-hero-subtitle">
727 <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
728 </p>
729 </div>
730
731 <div class="mxchat-content">
732 <!-- Import Settings Card -->
733 <div class="mxchat-card">
734 <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
735 <p><?php esc_html_e('Using the free Pinecone DB Manager add-on is recommended for high performance and a knowledge database of more than 500.', 'mxchat'); ?></p>
736
737 <!-- Tab Contents -->
738 <div class="mxchat-tab-contents">
739 <!-- Default Database Tab -->
740 <div id="default-db" class="mxchat-tab-content active">
741 <!-- Auto-Sync Settings -->
742 <div class="mxchat-settings-section">
743 <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
744 <p class="mxchat-description">
745 <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below. Works with Pinecone if enabled', 'mxchat'); ?>
746 </p>
747 <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
748 <div class="mxchat-toggle-group">
749 <div class="mxchat-toggle-container">
750 <label class="mxchat-toggle-switch">
751 <input type="checkbox"
752 name="mxchat_auto_sync_posts"
753 class="mxchat-autosave-field"
754 value="1"
755 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
756 <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
757 <span class="mxchat-toggle-slider"></span>
758 </label>
759 <span class="mxchat-toggle-label">
760 <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
761 </span>
762 </div>
763 <div class="mxchat-toggle-container">
764 <label class="mxchat-toggle-switch">
765 <input type="checkbox"
766 name="mxchat_auto_sync_pages"
767 class="mxchat-autosave-field"
768 value="1"
769 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
770 <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
771 <span class="mxchat-toggle-slider"></span>
772 </label>
773 <span class="mxchat-toggle-label">
774 <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
775 </span>
776 </div>
777 </div>
778 </div>
779 </div>
780
781 <!-- Import Methods -->
782 <div class="mxchat-import-methods">
783 <div class="mxchat-method-card">
784 <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
785 <p><?php esc_html_e('Import all content from your sitemap automatically.', 'mxchat'); ?></p>
786 </div>
787 <div class="mxchat-method-card">
788 <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
789 <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
790 </div>
791 <div class="mxchat-method-card">
792 <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
793 <p><?php esc_html_e('Import content from any specific webpage.', 'mxchat'); ?></p>
794 </div>
795 <div class="mxchat-method-card">
796 <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
797 <p><?php esc_html_e('Directly submit content to be vectorized.', 'mxchat'); ?></p>
798 </div>
799 </div>
800
801 <!-- Import Form -->
802 <?php if (!$is_processing) : ?>
803 <div class="mxchat-import-form">
804 <form id="mxchat-sitemap-form" method="post"
805 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
806 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
807 <div class="mxchat-url-input-group">
808 <input type="url"
809 name="sitemap_url"
810 id="sitemap_url"
811 placeholder="<?php esc_attr_e('Enter Sitemap, PDF, or Webpage URL', 'mxchat'); ?>"
812 required />
813 <button type="submit"
814 name="submit_sitemap"
815 class="mxchat-button-primary">
816 <?php esc_html_e('Import', 'mxchat'); ?>
817 </button>
818 </div>
819 </form>
820 </div>
821 <?php endif; ?>
822
823 <!-- Processing Status -->
824 <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
825 <div class="mxchat-status-card">
826 <div class="mxchat-status-header">
827 <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
828 <?php if ($is_processing) : ?>
829 <form method="post" class="mxchat-stop-form"
830 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
831 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
832 <button type="submit" name="stop_processing" class="mxchat-button-secondary">
833 <?php esc_html_e('Stop Processing', 'mxchat'); ?>
834 </button>
835 </form>
836 <?php endif; ?>
837 </div>
838 <div class="mxchat-progress-bar">
839 <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
840 </div>
841 <div class="mxchat-status-details">
842 <p><?php printf(
843 esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
844 absint($pdf_status['processed_pages']),
845 absint($pdf_status['total_pages']),
846 absint($pdf_status['percentage'])
847 ); ?></p>
848 <p><?php printf(
849 esc_html__('Status: %s', 'mxchat'),
850 esc_html(ucfirst($pdf_status['status']))
851 ); ?></p>
852 <p><?php printf(
853 esc_html__('Last update: %s', 'mxchat'),
854 esc_html($pdf_status['last_update'])
855 ); ?></p>
856 </div>
857 </div>
858 <?php endif; ?>
859
860 <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
861 <div class="mxchat-status-card">
862 <div class="mxchat-status-header">
863 <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
864 <?php if ($is_processing) : ?>
865 <form method="post" class="mxchat-stop-form"
866 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
867 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
868 <button type="submit" name="stop_processing" class="mxchat-button-secondary">
869 <?php esc_html_e('Stop Processing', 'mxchat'); ?>
870 </button>
871 </form>
872 <?php endif; ?>
873 </div>
874 <div class="mxchat-progress-bar">
875 <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
876 </div>
877 <div class="mxchat-status-details">
878 <p><?php printf(
879 esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
880 absint($sitemap_status['processed_urls']),
881 absint($sitemap_status['total_urls']),
882 absint($sitemap_status['percentage'])
883 ); ?></p>
884 <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
885 <div class="mxchat-error-notice">
886 <?php if (!empty($sitemap_status['error'])) : ?>
887 <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
888 <?php endif; ?>
889 <?php if (!empty($sitemap_status['last_error'])) : ?>
890 <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
891 <?php endif; ?>
892 </div>
893 <?php endif; ?>
894 </div>
895 </div>
896 <?php endif; ?>
897 </div>
898
899
900 </div>
901 </div>
902
903 <!-- Direct Content Submission Card -->
904 <div class="mxchat-card">
905 <div class="mxchat-card-header">
906 <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
907 </div>
908 <form id="mxchat-content-form" method="post"
909 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
910 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
911 <div class="mxchat-form-group">
912 <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
913 <textarea name="article_content" id="article_content" required
914 placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
915 </div>
916 <div class="mxchat-form-group">
917 <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
918 <input type="url" name="article_url" id="article_url"
919 placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
920 </div>
921 <button type="submit" name="submit_content" class="mxchat-button-primary">
922 <?php esc_html_e('Submit Content', 'mxchat'); ?>
923 </button>
924 </form>
925 </div>
926
927 <!-- Knowledge Base Table Card -->
928 <div class="mxchat-card">
929 <div class="mxchat-card-header">
930 <h2><?php esc_html_e('Knowledge Base', 'mxchat'); ?></h2>
931 <div class="mxchat-header-actions">
932 <!-- Search -->
933 <form method="get" id="knowledge-search" class="mxchat-search-form">
934 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
935 <input type="hidden" name="page" value="mxchat-prompts" />
936 <div class="mxchat-search-group">
937 <span class="dashicons dashicons-search"></span>
938 <input type="text"
939 name="search"
940 placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
941 value="<?php echo esc_attr($search_query); ?>" />
942 </div>
943 </form>
944
945 <!-- Delete All -->
946 <form method="post"
947 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
948 class="mxchat-delete-form"
949 onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
950 <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
951 <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
952 <span class="dashicons dashicons-trash"></span>
953 <?php esc_html_e('Delete All', 'mxchat'); ?>
954 </button>
955 </form>
956 </div>
957 </div>
958
959 <!-- Table -->
960 <div class="mxchat-table-wrapper">
961 <table class="mxchat-records-table">
962 <thead>
963 <tr>
964 <th><?php esc_html_e('ID', 'mxchat'); ?></th>
965 <th><?php esc_html_e('Content', 'mxchat'); ?></th>
966 <th><?php esc_html_e('Source', 'mxchat'); ?></th>
967 <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
968 </tr>
969 </thead>
970 <tbody>
971 <?php if ($prompts) : ?>
972 <?php foreach ($prompts as $prompt) : ?>
973 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
974 <td><?php echo esc_html($prompt->id); ?></td>
975 <td class="mxchat-content-cell">
976 <div class="content-view">
977 <?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?>
978 </div>
979 <textarea class="content-edit" style="display:none;">
980 <?php echo esc_textarea($prompt->article_content); ?>
981 </textarea>
982 </td>
983 <td class="mxchat-url-cell">
984 <?php if (!empty($prompt->source_url)) : ?>
985 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
986 <span class="dashicons dashicons-external"></span>
987 <?php esc_html_e('View Source', 'mxchat'); ?>
988 </a>
989 <?php else : ?>
990 <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
991 <?php endif; ?>
992 </td>
993 <td class="mxchat-actions-cell">
994 <button class="mxchat-button-icon edit-button"
995 data-id="<?php echo esc_attr($prompt->id); ?>">
996 <span class="dashicons dashicons-edit"></span>
997 </button>
998 <button class="mxchat-button-icon save-button"
999 data-id="<?php echo esc_attr($prompt->id); ?>"
1000 style="display:none;"
1001 data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
1002 <span class="dashicons dashicons-saved"></span>
1003 </button>
1004 <a href="<?php echo esc_url(admin_url(
1005 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1006 . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1007 )); ?>"
1008 class="mxchat-button-icon delete-button"
1009 onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1010 <span class="dashicons dashicons-trash"></span>
1011 </a>
1012 </td>
1013 </tr>
1014 <?php endforeach; ?>
1015 <?php else : ?>
1016 <tr>
1017 <td colspan="4" class="mxchat-no-records">
1018 <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1019 </td>
1020 </tr>
1021 <?php endif; ?>
1022 </tbody>
1023 </table>
1024 </div>
1025
1026 <?php if ($page_links) : ?>
1027 <div class="mxchat-pagination">
1028 <?php echo wp_kses_post($page_links); ?>
1029 </div>
1030 <?php endif; ?>
1031 </div>
1032 </div>
1033 </div>
1034 <?php
1035 }
1036
1037
1038 public function mxchat_handle_delete_all_prompts() {
1039 // Verify nonce
1040 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
1041 wp_die(__('Nonce verification failed.', 'mxchat'));
1042 }
1043
1044 // Check permissions
1045 if (!current_user_can('manage_options')) {
1046 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
1047 }
1048
1049 global $wpdb;
1050 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1051
1052 // Delete all prompts from the table
1053 $wpdb->query("DELETE FROM {$table_name}");
1054
1055 // Clear relevant cache
1056 wp_cache_delete('all_prompts', 'mxchat_prompts');
1057
1058 // Redirect back with a success message
1059 $redirect_url = add_query_arg(array(
1060 'page' => 'mxchat-prompts',
1061 'all_deleted' => 'true'
1062 ), admin_url('admin.php'));
1063
1064 wp_safe_redirect($redirect_url);
1065 exit;
1066 }
1067 public function mxchat_handle_delete_prompt() {
1068 // Sanitize and validate nonce
1069 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
1070 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
1071 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1072 }
1073
1074 // Check permissions
1075 if (!current_user_can('manage_options')) {
1076 wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
1077 }
1078
1079 // Validate and sanitize ID parameter
1080 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
1081 if ($id <= 0) {
1082 wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
1083 }
1084
1085 global $wpdb;
1086 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1087
1088 // Clear cache and delete prompt
1089 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
1090 $wpdb->delete($table_name, array('id' => $id), array('%d'));
1091
1092 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1093 exit;
1094 }
1095 public function mxchat_generate_embedding($text) {
1096 $options = get_option('mxchat_options');
1097 $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1098
1099 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1100 'body' => wp_json_encode(array(
1101 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1102 'input' => $text
1103 )),
1104 'headers' => array(
1105 'Authorization' => 'Bearer ' . $api_key,
1106 'Content-Type' => esc_html__('application/json', 'mxchat')
1107 ),
1108 ));
1109
1110 if (is_wp_error($response)) {
1111 return null;
1112 }
1113
1114 $response_data = json_decode(wp_remote_retrieve_body($response), true);
1115 return $response_data['data'][0]['embedding'] ?? null;
1116 }
1117 public function mxchat_delete_chat_history() {
1118 if (!current_user_can('manage_options')) {
1119 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1120 wp_die();
1121 }
1122
1123 check_ajax_referer('mxchat_delete_chat_history', 'security');
1124
1125 global $wpdb;
1126 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1127
1128 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
1129 foreach ($_POST['delete_session_ids'] as $session_id) {
1130 $session_id_sanitized = sanitize_text_field($session_id);
1131
1132 // Clear relevant cache before deletion
1133 $cache_key = 'chat_session_' . $session_id_sanitized;
1134 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
1135
1136 // Perform the deletion
1137 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
1138 }
1139
1140 // Optionally, clear a general cache if you have one
1141 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
1142
1143 echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]);
1144 } else {
1145 echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
1146 }
1147
1148 wp_die();
1149 }
1150
1151
1152 public function mxchat_save_inline_prompt() {
1153 // Check for nonce security
1154 check_ajax_referer('mxchat_save_inline_nonce');
1155
1156 // Verify permissions
1157 if (!current_user_can('manage_options')) {
1158 wp_send_json_error(esc_html__('Permission denied.', 'mxchat'));
1159 return;
1160 }
1161
1162 global $wpdb;
1163 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1164
1165 // Validate and sanitize input data
1166 $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1167 $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1168 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1169
1170 if ($prompt_id > 0 && !empty($article_content)) {
1171 // Re-generate the embedding vector for the updated content
1172 $embedding_vector = $this->mxchat_generate_embedding($article_content);
1173
1174 if (is_array($embedding_vector)) {
1175 // Serialize the embedding vector before storing it
1176 $embedding_vector_serialized = serialize($embedding_vector);
1177
1178 // Update the prompt in the database
1179 $updated = $wpdb->update(
1180 $table_name,
1181 array(
1182 'article_content' => $article_content,
1183 'embedding_vector' => $embedding_vector_serialized,
1184 'source_url' => $article_url,
1185 ),
1186 array('id' => $prompt_id),
1187 array('%s', '%s', '%s'),
1188 array('%d')
1189 );
1190
1191 if ($updated !== false) {
1192 wp_send_json_success();
1193 } else {
1194 wp_send_json_error(esc_html__('Database update failed.', 'mxchat'));
1195 }
1196 } else {
1197 wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat'));
1198 }
1199 } else {
1200 wp_send_json_error(esc_html__('Invalid data.', 'mxchat'));
1201 }
1202 }
1203
1204
1205
1206 // Add this method to handle post updates
1207 public function handle_post_update($post_id, $post, $update) {
1208 // Basic validation checks
1209 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1210 return;
1211 }
1212
1213 // Only process published content
1214 if (!in_array($post->post_status, array('publish'))) {
1215 return;
1216 }
1217
1218 // Check if sync is enabled for this post type
1219 $post_type = $post->post_type;
1220 if (!in_array($post_type, array('post', 'page'))) {
1221 return;
1222 }
1223
1224 $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1225 if (get_option($sync_option) != '1') {
1226 return;
1227 }
1228
1229 // Prepare content and URL
1230 $content = wp_strip_all_tags($post->post_content);
1231 $url = get_permalink($post_id);
1232
1233 // Generate embedding vector
1234 $embedding_vector = $this->mxchat_generate_embedding($content);
1235 if (!$embedding_vector) {
1236 return;
1237 }
1238
1239 // Check for Pinecone addon and its settings
1240 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1241 $use_pinecone = false;
1242
1243 if ($pinecone_settings && is_array($pinecone_settings)) {
1244 // Check if Pinecone is enabled and all required settings are present
1245 $use_pinecone = (
1246 isset($pinecone_settings['mxchat_use_pinecone']) &&
1247 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1248 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1249 !empty($pinecone_settings['mxchat_pinecone_host']) &&
1250 !empty($pinecone_settings['mxchat_pinecone_index']) &&
1251 !empty($pinecone_settings['mxchat_pinecone_environment'])
1252 );
1253 }
1254
1255 if ($use_pinecone) {
1256 // Use Pinecone
1257 $pinecone_result = $this->store_in_pinecone_main(
1258 $embedding_vector,
1259 $content,
1260 $url,
1261 $pinecone_settings['mxchat_pinecone_api_key'],
1262 $pinecone_settings['mxchat_pinecone_environment'],
1263 $pinecone_settings['mxchat_pinecone_index']
1264 );
1265
1266 if (!$pinecone_result['success']) {
1267 //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1268 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1269 }
1270 } else {
1271 // Fallback to WordPress DB storage
1272 $this->store_in_wordpress_db($content, $url, $embedding_vector);
1273 }
1274 }
1275 // Modified storage function to add type field
1276 private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1277 $vector_id = $vector_id ?: md5($url);
1278 $options = get_option('mxchat_pinecone_addon_options');
1279 $host = $options['mxchat_pinecone_host'] ?? '';
1280
1281 if (empty($host)) {
1282 return array(
1283 'success' => false,
1284 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1285 );
1286 }
1287
1288 // Determine if this is a product URL
1289 $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1290
1291 $api_endpoint = "https://{$host}/vectors/upsert";
1292 $request_body = array(
1293 'vectors' => array(
1294 array(
1295 'id' => $vector_id,
1296 'values' => $embedding_vector,
1297 'metadata' => array(
1298 'text' => $content,
1299 'source_url' => $url,
1300 'type' => $is_product ? 'product' : 'content',
1301 'last_updated' => time()
1302 )
1303 )
1304 )
1305 );
1306
1307 $response = wp_remote_post($api_endpoint, array(
1308 'headers' => array(
1309 'Api-Key' => $api_key,
1310 'accept' => 'application/json',
1311 'content-type' => 'application/json'
1312 ),
1313 'body' => wp_json_encode($request_body),
1314 'timeout' => 30,
1315 'data_format' => 'body'
1316 ));
1317
1318 if (is_wp_error($response)) {
1319 return array(
1320 'success' => false,
1321 'message' => $response->get_error_message()
1322 );
1323 }
1324
1325 $response_code = wp_remote_retrieve_response_code($response);
1326 if ($response_code !== 200) {
1327 $body = wp_remote_retrieve_body($response);
1328 return array(
1329 'success' => false,
1330 'message' => sprintf(
1331 'Pinecone API error (HTTP %d): %s',
1332 $response_code,
1333 $body
1334 )
1335 );
1336 }
1337
1338 return array(
1339 'success' => true,
1340 'message' => 'Successfully stored in Pinecone'
1341 );
1342 }
1343 // Helper method for WordPress DB storage
1344 private function store_in_wordpress_db($content, $url, $embedding_vector) {
1345 global $wpdb;
1346 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1347
1348 $existing_entry = $wpdb->get_row(
1349 $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url)
1350 );
1351
1352 if ($existing_entry) {
1353 $wpdb->update(
1354 $table_name,
1355 array(
1356 'article_content' => $content,
1357 'embedding_vector' => serialize($embedding_vector),
1358 'timestamp' => current_time('mysql')
1359 ),
1360 array('id' => $existing_entry->id),
1361 array('%s', '%s', '%s')
1362 );
1363 } else {
1364 $wpdb->insert(
1365 $table_name,
1366 array(
1367 'article_content' => $content,
1368 'source_url' => $url,
1369 'embedding_vector' => serialize($embedding_vector),
1370 'timestamp' => current_time('mysql')
1371 ),
1372 array('%s', '%s', '%s', '%s')
1373 );
1374 }
1375 }
1376 /**
1377 * Handle deletion of posts and pages from both Pinecone and WordPress DB
1378 *
1379 * @param int $post_id The ID of the post being deleted
1380 * @return void
1381 */
1382 public function mxchat_handle_post_delete($post_id) {
1383 // Get post data before it's deleted
1384 $post = get_post($post_id);
1385
1386 // Basic validation
1387 if (!$post || wp_is_post_revision($post_id)) {
1388 return;
1389 }
1390
1391 // Check post type
1392 $post_type = $post->post_type;
1393 if (!in_array($post_type, array('post', 'page'))) {
1394 return;
1395 }
1396
1397 // Check if sync is enabled for this post type
1398 $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1399 if (get_option($sync_option) != '1') {
1400 return;
1401 }
1402
1403 // Get the URL before post is deleted
1404 $source_url = get_permalink($post_id);
1405 if (!$source_url) {
1406 //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1407 return;
1408 }
1409
1410 // Check for Pinecone addon and its settings
1411 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1412 $use_pinecone = false;
1413 if ($pinecone_settings && is_array($pinecone_settings)) {
1414 $use_pinecone = (
1415 isset($pinecone_settings['mxchat_use_pinecone']) &&
1416 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1417 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1418 !empty($pinecone_settings['mxchat_pinecone_host']) &&
1419 !empty($pinecone_settings['mxchat_pinecone_index']) &&
1420 !empty($pinecone_settings['mxchat_pinecone_environment'])
1421 );
1422 }
1423
1424 $deletion_successful = false;
1425
1426 if ($use_pinecone) {
1427 try {
1428 $pinecone_result = $this->delete_from_pinecone(
1429 array($source_url),
1430 $pinecone_settings['mxchat_pinecone_api_key'],
1431 $pinecone_settings['mxchat_pinecone_environment'],
1432 $pinecone_settings['mxchat_pinecone_index']
1433 );
1434
1435 if (!$pinecone_result['success']) {
1436 //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1437 } else {
1438 $deletion_successful = true;
1439 }
1440 } catch (Exception $e) {
1441 //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1442 }
1443 }
1444
1445 // Always attempt WordPress DB deletion, regardless of Pinecone status
1446 try {
1447 global $wpdb;
1448 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1449
1450 $result = $wpdb->delete(
1451 $table_name,
1452 array('source_url' => $source_url),
1453 array('%s')
1454 );
1455
1456 if ($result === false) {
1457 //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1458 } else {
1459 $deletion_successful = true;
1460 }
1461 } catch (Exception $e) {
1462 //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1463 }
1464
1465 if (!$deletion_successful) {
1466 //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1467 }
1468 }
1469
1470
1471 public function mxchat_handle_content_submission() {
1472 // Check if the form was submitted and the user has permission.
1473 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1474 return;
1475 }
1476
1477 // Verify the nonce.
1478 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1479 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1480 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1481 }
1482
1483 // Sanitize the inputs.
1484 $article_content = sanitize_textarea_field($_POST['article_content']);
1485 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1486
1487 // Generate the embedding vector.
1488 $embedding_vector = $this->mxchat_generate_embedding($article_content);
1489 if (!is_array($embedding_vector)) {
1490 set_transient('mxchat_admin_notice_error',
1491 esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1492 30
1493 );
1494 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1495 exit;
1496 }
1497
1498 // Store in the WordPress database.
1499 global $wpdb;
1500 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1501
1502 // Ensure the source_url column exists.
1503 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1504 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1505 }
1506
1507 // Serialize and store the embedding vector.
1508 $embedding_vector_serialized = serialize($embedding_vector);
1509 $inserted = $wpdb->insert(
1510 $table_name,
1511 array(
1512 'article_content' => $article_content,
1513 'embedding_vector' => $embedding_vector_serialized,
1514 'source_url' => $article_url,
1515 ),
1516 array('%s', '%s', '%s')
1517 );
1518
1519 if ($inserted === false) {
1520 set_transient('mxchat_admin_notice_error',
1521 esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1522 30
1523 );
1524 } else {
1525 set_transient('mxchat_admin_notice_success',
1526 esc_html__('Content successfully submitted!', 'mxchat'),
1527 30
1528 );
1529 }
1530
1531 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1532 exit;
1533 }
1534
1535 private function is_pdf_url($url, $response) {
1536 $content_type = wp_remote_retrieve_header($response, 'content-type');
1537 $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1538
1539 return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1540 }
1541 private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1542 if (!current_user_can('manage_options')) {
1543 //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat'));
1544 return false;
1545 }
1546
1547 $pdf_url = esc_url_raw($pdf_url);
1548 $upload_dir = wp_upload_dir();
1549
1550 if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1551 //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error'])));
1552 return false;
1553 }
1554
1555 $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1556 $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1557
1558 $response_body = wp_remote_retrieve_body($response);
1559 if (empty($response_body)) {
1560 //error_log(esc_html__('Empty PDF response body', 'mxchat'));
1561 return false;
1562 }
1563
1564 if (!wp_mkdir_p(dirname($pdf_path))) {
1565 //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path)));
1566 return false;
1567 }
1568
1569 try {
1570 file_put_contents($pdf_path, $response_body);
1571
1572 if (!file_exists($pdf_path)) {
1573 throw new Exception(__('Failed to save PDF file', 'mxchat'));
1574 }
1575
1576 $parser = new \Smalot\PdfParser\Parser();
1577 $pdf = $parser->parseFile($pdf_path);
1578 $total_pages = absint(count($pdf->getPages()));
1579
1580 if ($total_pages < 1) {
1581 throw new Exception(__('Invalid PDF: no pages found', 'mxchat'));
1582 }
1583
1584 wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1585 'pdf_path' => $pdf_path,
1586 'pdf_url' => $pdf_url,
1587 'total_pages' => $total_pages,
1588 'batch_size' => absint(15),
1589 'batch_pause' => absint(10)
1590 ));
1591
1592 $status_data = array(
1593 'total_pages' => $total_pages,
1594 'processed_pages' => 0,
1595 'status' => 'processing',
1596 'last_update' => time()
1597 );
1598
1599 set_transient(
1600 sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1601 array_map('sanitize_text_field', $status_data),
1602 DAY_IN_SECONDS
1603 );
1604
1605 return __('scheduled', 'mxchat');
1606
1607 } catch (Exception $e) {
1608 //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1609 if (file_exists($pdf_path)) {
1610 wp_delete_file($pdf_path);
1611 }
1612 return false;
1613 }
1614 }
1615 public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1616 // Validate inputs
1617 $pdf_path = sanitize_text_field($pdf_path);
1618 $pdf_url = esc_url_raw($pdf_url);
1619 $total_pages = absint($total_pages);
1620 $batch_size = absint($batch_size);
1621 $batch_pause = absint($batch_pause);
1622
1623 try {
1624 if (!file_exists($pdf_path)) {
1625 throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1626 }
1627
1628 $parser = new \Smalot\PdfParser\Parser();
1629 $pdf = $parser->parseFile($pdf_path);
1630 $pages = $pdf->getPages();
1631
1632 // Get current progress
1633 $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1634 $status = get_transient($status_key);
1635
1636 if (!$status || !is_array($status)) {
1637 throw new Exception('Invalid status data retrieved from transient');
1638 }
1639
1640 $start_page = absint($status['processed_pages']);
1641 $end_page = min($start_page + $batch_size, $total_pages);
1642
1643 $instance = new self(); // Create an instance of the class
1644
1645 for ($i = $start_page; $i < $end_page; $i++) {
1646 $text = $pages[$i]->getText();
1647 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1648
1649 if (!empty($sanitized_content)) {
1650 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1651 if (is_array($embedding_vector)) {
1652 $metadata = array(
1653 'document_type' => 'pdf',
1654 'total_pages' => $total_pages,
1655 'current_page' => $i + 1,
1656 'prev_page' => $i > 0 ? $i : null,
1657 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1658 'source_url' => $pdf_url
1659 );
1660
1661 $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1662 $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1663
1664 $options = get_option('mxchat_options');
1665 MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1666 }
1667 }
1668
1669 // Update progress with sanitized data
1670 $status['processed_pages'] = absint($i + 1);
1671 $status['last_update'] = time();
1672 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1673 }
1674
1675 // Schedule next batch if needed
1676 if ($end_page < $total_pages) {
1677 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
1678 'pdf_path' => $pdf_path,
1679 'pdf_url' => $pdf_url,
1680 'total_pages' => $total_pages,
1681 'batch_size' => $batch_size,
1682 'batch_pause' => $batch_pause
1683 ));
1684 } else {
1685 // Processing complete
1686 $status['status'] = 'complete';
1687 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1688 if (file_exists($pdf_path)) {
1689 wp_delete_file($pdf_path);
1690 }
1691 }
1692
1693 } catch (\Exception $e) {
1694 $status['status'] = 'error';
1695 $status['error'] = sanitize_text_field($e->getMessage());
1696 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1697 if (file_exists($pdf_path)) {
1698 wp_delete_file($pdf_path);
1699 }
1700 }
1701 }
1702 public function get_pdf_processing_status($pdf_url) {
1703 $pdf_url = esc_url_raw($pdf_url);
1704 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1705
1706 if (!$status || !is_array($status)) {
1707 return false;
1708 }
1709
1710 return array(
1711 'total_pages' => absint($status['total_pages']),
1712 'processed_pages' => absint($status['processed_pages']),
1713 'percentage' => ($status['total_pages'] > 0)
1714 ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1715 : 0,
1716 'status' => sanitize_text_field($status['status']),
1717 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1718 );
1719 }
1720 public function mxchat_handle_sitemap_submission() {
1721 // Check if the form was submitted and verify permissions
1722 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
1723 wp_die(esc_html__('Unauthorized access', 'mxchat'));
1724 }
1725
1726 // Verify nonce
1727 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1728
1729 // Validate URL
1730 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
1731 set_transient('mxchat_admin_notice_error',
1732 esc_html__('Please provide a valid URL.', 'mxchat'),
1733 30
1734 );
1735 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1736 exit;
1737 }
1738
1739 $submitted_url = esc_url_raw($_POST['sitemap_url']);
1740 $response = wp_remote_get($submitted_url, array('timeout' => 30));
1741
1742 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1743 $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat');
1744 set_transient('mxchat_admin_notice_error',
1745 sprintf(
1746 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1747 esc_html($error_message)
1748 ),
1749 30
1750 );
1751 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1752 exit;
1753 }
1754
1755 $content_type = wp_remote_retrieve_header($response, 'content-type');
1756 $body_content = wp_remote_retrieve_body($response);
1757
1758 if (empty($body_content)) {
1759 set_transient('mxchat_admin_notice_error',
1760 esc_html__('Empty response received from URL.', 'mxchat'),
1761 30
1762 );
1763 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1764 exit;
1765 }
1766
1767 // Handle PDF URL
1768 if ($this->is_pdf_url($submitted_url, $response)) {
1769 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
1770
1771 if ($result === 'scheduled') {
1772 set_transient(
1773 'mxchat_last_pdf_url',
1774 sanitize_text_field($submitted_url),
1775 DAY_IN_SECONDS
1776 );
1777 set_transient('mxchat_admin_notice_info',
1778 esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1779 30
1780 );
1781 } else {
1782 set_transient('mxchat_admin_notice_error',
1783 esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
1784 30
1785 );
1786 }
1787
1788 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1789 exit;
1790 }
1791
1792 // Handle Sitemap XML
1793 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
1794 libxml_use_internal_errors(true);
1795 $xml = simplexml_load_string($body_content);
1796 $xml_errors = libxml_get_errors();
1797 libxml_clear_errors();
1798
1799 if ($xml === false || !empty($xml_errors)) {
1800 set_transient('mxchat_admin_notice_error',
1801 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1802 30
1803 );
1804 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1805 exit;
1806 }
1807
1808 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
1809
1810 if ($result === 'scheduled') {
1811 set_transient(
1812 'mxchat_last_sitemap_url',
1813 sanitize_text_field($submitted_url),
1814 DAY_IN_SECONDS
1815 );
1816 set_transient('mxchat_admin_notice_info',
1817 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1818 30
1819 );
1820 } else {
1821 set_transient('mxchat_admin_notice_error',
1822 esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
1823 30
1824 );
1825 }
1826
1827 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1828 exit;
1829 }
1830
1831 // Handle Regular URL
1832 $page_content = $this->mxchat_extract_main_content($body_content);
1833 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
1834
1835 if (empty($sanitized_content)) {
1836 set_transient('mxchat_admin_notice_error',
1837 esc_html__('No valid content found on the provided URL.', 'mxchat'),
1838 30
1839 );
1840 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1841 exit;
1842 }
1843
1844 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
1845 if (is_array($embedding_vector)) {
1846 MxChat_Utils::submit_content_to_db(
1847 $sanitized_content,
1848 $submitted_url,
1849 $this->options['api_key']
1850 );
1851 set_transient('mxchat_admin_notice_success',
1852 esc_html__('URL content successfully submitted!', 'mxchat'),
1853 30
1854 );
1855 } else {
1856 set_transient('mxchat_admin_notice_error',
1857 esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
1858 30
1859 );
1860 }
1861
1862 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1863 exit;
1864 }
1865 private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
1866 if (!current_user_can('manage_options')) {
1867 //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
1868 return false;
1869 }
1870
1871 try {
1872 $sitemap_url = esc_url_raw($sitemap_url);
1873
1874 if (!$xml || !is_object($xml)) {
1875 throw new Exception(__('Invalid XML object provided', 'mxchat'));
1876 }
1877
1878 $urls = [];
1879 foreach ($xml->url as $url_element) {
1880 $url = esc_url_raw((string)$url_element->loc);
1881 if ($url) {
1882 $urls[] = $url;
1883 }
1884 }
1885
1886 $total_urls = absint(count($urls));
1887
1888 if ($total_urls < 1) {
1889 throw new Exception(__('No valid URLs found in sitemap', 'mxchat'));
1890 }
1891
1892 wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
1893 'urls' => $urls,
1894 'sitemap_url' => $sitemap_url,
1895 'total_urls' => $total_urls,
1896 'batch_size' => absint(10),
1897 'batch_pause' => absint(5)
1898 ));
1899
1900 $status_data = array(
1901 'total_urls' => $total_urls,
1902 'processed_urls' => 0,
1903 'status' => 'processing',
1904 'last_update' => time()
1905 );
1906
1907 set_transient(
1908 sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
1909 array_map('sanitize_text_field', $status_data),
1910 DAY_IN_SECONDS
1911 );
1912
1913 return __('scheduled', 'mxchat');
1914
1915 } catch (\Exception $e) {
1916 //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1917 return false;
1918 }
1919 }
1920 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1921 // Validate inputs
1922 $sitemap_url = esc_url_raw($sitemap_url);
1923 $total_urls = absint($total_urls);
1924 $batch_size = absint($batch_size);
1925 $batch_pause = absint($batch_pause);
1926
1927 if (!is_array($urls) || empty($urls)) {
1928 return;
1929 }
1930
1931 try {
1932 $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
1933 $status = get_transient($status_key);
1934
1935 if (!$status || !is_array($status)) {
1936 throw new Exception('Invalid status data retrieved from transient');
1937 }
1938
1939 $start_url = absint($status['processed_urls']);
1940 $end_url = min($start_url + $batch_size, $total_urls);
1941 $instance = new self();
1942
1943 // Track failures in batch
1944 $batch_stats = [
1945 'processed' => 0,
1946 'failed' => 0,
1947 'last_error' => ''
1948 ];
1949
1950 for ($i = $start_url; $i < $end_url; $i++) {
1951 $page_url = esc_url_raw($urls[$i]);
1952 $page_response = wp_remote_get($page_url);
1953
1954 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1955 $batch_stats['failed']++;
1956 continue;
1957 }
1958
1959 $page_html = wp_remote_retrieve_body($page_response);
1960 $page_content = $instance->mxchat_extract_main_content($page_html);
1961 $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
1962
1963 if (!empty($sanitized_content)) {
1964 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
1965
1966 if (is_array($embedding_vector)) {
1967 $options = get_option('mxchat_options');
1968 $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
1969
1970 if (is_wp_error($submission_result)) {
1971 $batch_stats['failed']++;
1972 $batch_stats['last_error'] = $submission_result->get_error_message();
1973 continue;
1974 }
1975
1976 $batch_stats['processed']++;
1977 } else {
1978 $batch_stats['failed']++;
1979 $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
1980 }
1981 }
1982
1983 $status['processed_urls'] = absint($i + 1);
1984 $status['last_update'] = time();
1985 $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
1986 $status['last_error'] = $batch_stats['last_error'];
1987
1988 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1989 }
1990
1991 // If all URLs in this batch failed, stop processing
1992 if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
1993 $status['status'] = 'error';
1994 $status['error'] = sprintf(
1995 'Processing stopped: %d consecutive failures. Last error: %s',
1996 $batch_stats['failed'],
1997 $batch_stats['last_error']
1998 );
1999 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2000 return;
2001 }
2002
2003 if ($end_url < $total_urls) {
2004 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2005 'urls' => array_map('esc_url_raw', $urls),
2006 'sitemap_url' => $sitemap_url,
2007 'total_urls' => $total_urls,
2008 'batch_size' => $batch_size,
2009 'batch_pause' => $batch_pause,
2010 ));
2011 } else {
2012 $status['status'] = 'complete';
2013 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2014 }
2015 } catch (\Exception $e) {
2016 $status['status'] = 'error';
2017 $status['error'] = sanitize_text_field($e->getMessage());
2018 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2019 }
2020 }
2021
2022 public function get_sitemap_processing_status($sitemap_url) {
2023 $sitemap_url = esc_url_raw($sitemap_url);
2024 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2025
2026 if (!$status || !is_array($status)) {
2027 return false;
2028 }
2029
2030 return array(
2031 'total_urls' => absint($status['total_urls']),
2032 'processed_urls' => absint($status['processed_urls']),
2033 'failed_urls' => absint($status['failed_urls'] ?? 0),
2034 'percentage' => ($status['total_urls'] > 0)
2035 ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2036 : 0,
2037 'status' => sanitize_text_field($status['status']),
2038 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2039 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2040 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
2041 );
2042 }
2043
2044 public function mxchat_stop_processing() {
2045 // Verify permissions
2046 if (!current_user_can('manage_options')) {
2047 wp_die(esc_html__('Unauthorized access', 'mxchat'));
2048 }
2049
2050 // Verify nonce
2051 check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
2052
2053 // Get the last sitemap URL and clear its transient
2054 $sitemap_url = get_transient('mxchat_last_sitemap_url');
2055 if ($sitemap_url) {
2056 delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
2057 delete_transient('mxchat_last_sitemap_url');
2058 }
2059
2060 // Get the last PDF URL and clear its transient
2061 $pdf_url = get_transient('mxchat_last_pdf_url');
2062 if ($pdf_url) {
2063 delete_transient('mxchat_pdf_status_' . md5($pdf_url));
2064 delete_transient('mxchat_last_pdf_url');
2065 }
2066
2067 // Unschedule any pending sitemap events
2068 $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
2069 if ($timestamp) {
2070 wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
2071 }
2072
2073 // Redirect back with a success message
2074 set_transient('mxchat_admin_notice_success',
2075 esc_html__('Processing has been stopped successfully.', 'mxchat'),
2076 30
2077 );
2078 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2079 exit;
2080 }
2081 private function mxchat_sanitize_content_for_api($content) {
2082 // Remove script, style tags, and HTML comments
2083 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2084 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2085 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2086
2087 // Remove all HTML tags and decode HTML entities
2088 $content = wp_strip_all_tags($content);
2089 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2090
2091 // Trim and normalize whitespace
2092 $content = trim(preg_replace('/\s+/', ' ', $content));
2093
2094 return $content;
2095 }
2096
2097
2098
2099
2100 public function mxchat_handle_product_change($post_id, $post, $update) {
2101 if ($post->post_type !== 'product') {
2102 return;
2103 }
2104
2105 if ($post->post_status === 'publish') {
2106 add_action('shutdown', function() use ($post_id) {
2107 $product = wc_get_product($post_id);
2108 if ($product) {
2109 $this->mxchat_store_product_embedding($product);
2110 }
2111 });
2112 }
2113 }
2114
2115 private function mxchat_store_product_embedding($product) {
2116 if (!isset($this->options['enable_woocommerce_integration']) ||
2117 !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2118 return;
2119 }
2120
2121 $source_url = get_permalink($product->get_id());
2122
2123 // Build description
2124 $regular_price = $product->get_regular_price();
2125 $sale_price = $product->get_sale_price();
2126 $description = $product->get_description() . "\n\n" .
2127 esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" .
2128 esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" .
2129 esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" .
2130 esc_html__('SKU:', 'mxchat') . " " . $product->get_sku();
2131
2132 // Generate embedding vector
2133 $embedding_vector = $this->mxchat_generate_embedding($description);
2134 if (!is_array($embedding_vector)) {
2135 return;
2136 }
2137
2138 // Check for Pinecone addon and its settings
2139 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2140 $use_pinecone = false;
2141
2142 if ($pinecone_settings && is_array($pinecone_settings)) {
2143 $use_pinecone = (
2144 isset($pinecone_settings['mxchat_use_pinecone']) &&
2145 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2146 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2147 !empty($pinecone_settings['mxchat_pinecone_host']) &&
2148 !empty($pinecone_settings['mxchat_pinecone_index']) &&
2149 !empty($pinecone_settings['mxchat_pinecone_environment'])
2150 );
2151 }
2152
2153 if ($use_pinecone) {
2154 $result = $this->store_in_pinecone_main(
2155 $embedding_vector,
2156 $description,
2157 $source_url,
2158 $pinecone_settings['mxchat_pinecone_api_key'],
2159 $pinecone_settings['mxchat_pinecone_environment'],
2160 $pinecone_settings['mxchat_pinecone_index']
2161 );
2162
2163 if (!$result['success']) {
2164 // Fallback to WordPress DB if Pinecone storage fails
2165 $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2166 }
2167 } else {
2168 // Use WordPress DB storage
2169 $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2170 }
2171 }
2172
2173 public function mxchat_handle_product_delete($post_id) {
2174 if (get_post_type($post_id) !== 'product') {
2175 return;
2176 }
2177
2178 $source_url = get_permalink($post_id);
2179
2180 // Check for Pinecone addon and its settings
2181 $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2182 $use_pinecone = false;
2183
2184 if ($pinecone_settings && is_array($pinecone_settings)) {
2185 $use_pinecone = (
2186 isset($pinecone_settings['mxchat_use_pinecone']) &&
2187 $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2188 !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2189 !empty($pinecone_settings['mxchat_pinecone_host']) &&
2190 !empty($pinecone_settings['mxchat_pinecone_index']) &&
2191 !empty($pinecone_settings['mxchat_pinecone_environment'])
2192 );
2193 }
2194
2195 if ($use_pinecone) {
2196 $this->delete_from_pinecone(
2197 array($source_url),
2198 $pinecone_settings['mxchat_pinecone_api_key'],
2199 $pinecone_settings['mxchat_pinecone_environment'],
2200 $pinecone_settings['mxchat_pinecone_index']
2201 );
2202 } else {
2203 // Fallback to WordPress DB deletion
2204 global $wpdb;
2205 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2206 $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2207 }
2208 }
2209
2210 /**
2211 * Delete vectors from Pinecone by source URL
2212 *
2213 * @param array $urls Array of source URLs to delete
2214 * @param string $api_key Pinecone API key
2215 * @param string $environment Pinecone environment
2216 * @param string $index_name Pinecone index name
2217 * @return array Associative array with 'success' boolean and 'message' string
2218 */
2219 private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2220 // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2221 $options = get_option('mxchat_pinecone_addon_options');
2222 $host = $options['mxchat_pinecone_host'] ?? '';
2223
2224 if (empty($host)) {
2225 return array(
2226 'success' => false,
2227 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2228 );
2229 }
2230
2231 // Build API endpoint using the configured host
2232 $api_endpoint = "https://{$host}/vectors/delete";
2233
2234 // Create vector IDs from URLs (matching your store method's ID generation)
2235 $vector_ids = array_map('md5', $urls);
2236
2237 // Prepare the delete request body
2238 $request_body = array(
2239 'ids' => $vector_ids,
2240 'filter' => array(
2241 'source_url' => array(
2242 '$in' => $urls
2243 )
2244 )
2245 );
2246
2247 // Make the deletion request
2248 $response = wp_remote_post($api_endpoint, array(
2249 'headers' => array(
2250 'Api-Key' => $api_key,
2251 'accept' => 'application/json',
2252 'content-type' => 'application/json'
2253 ),
2254 'body' => wp_json_encode($request_body),
2255 'timeout' => 30,
2256 'data_format' => 'body'
2257 ));
2258
2259 // Handle WordPress HTTP API errors
2260 if (is_wp_error($response)) {
2261 return array(
2262 'success' => false,
2263 'message' => $response->get_error_message()
2264 );
2265 }
2266
2267 // Check response status
2268 $response_code = wp_remote_retrieve_response_code($response);
2269 if ($response_code !== 200) {
2270 $body = wp_remote_retrieve_body($response);
2271 return array(
2272 'success' => false,
2273 'message' => sprintf(
2274 'Pinecone API error (HTTP %d): %s',
2275 $response_code,
2276 $body
2277 )
2278 );
2279 }
2280
2281 // Parse response body
2282 $body = wp_remote_retrieve_body($response);
2283 $response_data = json_decode($body, true);
2284
2285 // Final validation of the response
2286 if (json_last_error() !== JSON_ERROR_NONE) {
2287 return array(
2288 'success' => false,
2289 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2290 );
2291 }
2292
2293 return array(
2294 'success' => true,
2295 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2296 );
2297 }
2298
2299
2300 public function mxchat_fetch_chat_history() {
2301 global $wpdb;
2302 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2303
2304 if (!current_user_can('manage_options')) {
2305 wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2306 }
2307
2308 // First get unique session IDs ordered by most recent message in each session
2309 $session_ids = $wpdb->get_col(
2310 $wpdb->prepare(
2311 "SELECT DISTINCT session_id
2312 FROM {$table_name}
2313 GROUP BY session_id
2314 ORDER BY MAX(timestamp) DESC"
2315 )
2316 );
2317
2318 if (empty($session_ids)) {
2319 wp_die(esc_html__('No chat history available.', 'mxchat'));
2320 }
2321
2322 ob_start();
2323 echo '<div class="mxchat-transcript">';
2324
2325 // Iterate through sessions from newest to oldest
2326 foreach ($session_ids as $session_id) {
2327 // Get the email associated with this session (if available)
2328 $email = $wpdb->get_var(
2329 $wpdb->prepare(
2330 "SELECT user_email
2331 FROM {$table_name}
2332 WHERE session_id = %s AND user_email != ''
2333 ORDER BY timestamp ASC
2334 LIMIT 1",
2335 $session_id
2336 )
2337 );
2338
2339 // Debug: Check email retrieval
2340 if (empty($email)) {
2341 //error_log("No email found for session {$session_id}");
2342 } else {
2343 //error_log("Email for session {$session_id}: " . $email);
2344 }
2345
2346 // Get messages for this session ordered by timestamp
2347 $messages = $wpdb->get_results(
2348 $wpdb->prepare(
2349 "SELECT * FROM {$table_name}
2350 WHERE session_id = %s
2351 ORDER BY timestamp ASC",
2352 $session_id
2353 )
2354 );
2355
2356 // Start session block
2357 echo '<div class="mxchat-session">';
2358 echo '<div class="mxchat-session-header">';
2359 // Wrap checkbox and session ID in one block
2360 echo '<div class="mxchat-session-id">';
2361 echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
2362 echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
2363 echo '</div>';
2364
2365 // Place email directly below the session ID block
2366 if (!empty($email)) {
2367 echo '<div class="mxchat-session-email">';
2368 echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
2369 echo '</div>';
2370 }
2371 echo '</div>';
2372
2373 echo '<div class="mxchat-messages">';
2374
2375 // Display messages for this session
2376 foreach ($messages as $transcript) {
2377 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
2378
2379 // Determine message styling
2380 switch ($transcript->role) {
2381 case 'assistant':
2382 case 'bot':
2383 $message_class = 'bot-message';
2384 $display_role = esc_html__('Chatbot', 'mxchat');
2385 break;
2386 case 'user':
2387 $message_class = 'user-message';
2388 $display_role = !empty($transcript->user_identifier)
2389 ? sanitize_text_field($transcript->user_identifier)
2390 : esc_html__('User', 'mxchat');
2391 break;
2392 case 'agent':
2393 $message_class = 'agent-message';
2394 $display_role = esc_html__('Agent', 'mxchat');
2395 break;
2396 default:
2397 $message_class = 'unknown-message';
2398 $display_role = esc_html__('Unknown', 'mxchat');
2399 }
2400
2401 // Process message content
2402 $message_content = wp_kses(
2403 stripslashes($transcript->message),
2404 [
2405 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2406 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2407 'a' => ['href' => [], 'title' => []]
2408 ]
2409 );
2410 $message_content = nl2br($message_content);
2411
2412 // Render message
2413 echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
2414 echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
2415 echo '<div class="mxchat-message-content">' . $message_content . '</div>';
2416 echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
2417 echo '</div>';
2418 }
2419
2420 // Close session block
2421 echo '</div></div>';
2422 }
2423
2424 echo '</div>';
2425 $output = ob_get_clean();
2426 echo $output;
2427 wp_die();
2428 }
2429
2430
2431 public function mxchat_create_activation_page() {
2432 $license_status = get_option('mxchat_license_status', 'inactive');
2433 $license_error = get_option('mxchat_license_error', '');
2434 ?>
2435 <div class="wrap mxchat-admin-activation">
2436 <div class="mxchat-pro-hero">
2437 <h1 class="pro-title">
2438 <span class="pro-gradient-text">Activate</span> MxChat Pro
2439 </h1>
2440 <p class="pro-subtitle">
2441 <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2442 </p>
2443 </div>
2444
2445 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
2446 <div class="error notice">
2447 <p><?php echo esc_html($license_error); ?></p>
2448 </div>
2449 <?php endif; ?>
2450
2451 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2452 <div class="mxchat-pro-form-container">
2453 <table class="form-table">
2454 <tr valign="top">
2455 <th scope="row"><?php esc_html_e('Email Address', 'mxchat'); ?></th>
2456 <td>
2457 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text mxchat-pro-input" required />
2458 </td>
2459 </tr>
2460 <tr valign="top">
2461 <th scope="row"><?php esc_html_e('Activation Key', 'mxchat'); ?></th>
2462 <td>
2463 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text mxchat-pro-input" required />
2464 </td>
2465 </tr>
2466 </table>
2467 <?php if ($license_status !== 'active'): ?>
2468 <div class="mxchat-pro-button-container">
2469 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2470 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
2471 </div>
2472 <?php endif; ?>
2473 </div>
2474 </form>
2475 <!-- License Status Display -->
2476 <div class="mxchat-pro-status">
2477 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2478 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2479 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2480 </span>
2481 </h3>
2482 </div>
2483
2484 </div>
2485 <?php
2486 }
2487
2488 public function mxchat_intents_page_html() {
2489 if (!current_user_can('manage_options')) {
2490 return;
2491 }
2492
2493 // Keep existing data fetching logic
2494 global $wpdb;
2495 $table_name = $wpdb->prefix . 'mxchat_intents';
2496 $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
2497 $per_page = 20;
2498 $offset = ($page - 1) * $per_page;
2499
2500 // Success message
2501 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2502 echo '<div class="notice notice-success is-dismissible"><p>' .
2503 esc_html__('Intent updated successfully.', 'mxchat') .
2504 '</p></div>';
2505 }
2506
2507 // Filtering logic
2508 $where = '1=1';
2509 $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
2510 $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
2511
2512 if ($search_term) {
2513 $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
2514 $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
2515 $search_term_like, $search_term_like);
2516 }
2517
2518 if ($callback_filter) {
2519 $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
2520 }
2521
2522 // Pagination
2523 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2524 $total_pages = ceil($total_intents / $per_page);
2525
2526 // Get intents
2527 $intents = $wpdb->get_results($wpdb->prepare(
2528 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2529 $per_page, $offset
2530 ));
2531
2532 // Get callbacks
2533 $available_callbacks = $this->mxchat_get_available_callbacks();
2534
2535 ?>
2536 <div class="wrap mxchat-wrapper">
2537 <!-- Hero Section -->
2538 <div class="mxchat-hero">
2539 <h1 class="mxchat-main-title">
2540 <span class="mxchat-gradient-text">Intent</span> Manager
2541 </h1>
2542 <p class="mxchat-hero-subtitle">
2543 <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
2544 </p>
2545 </div>
2546
2547 <div class="mxchat-content">
2548 <!-- Add Intent Card -->
2549 <div class="mxchat-card">
2550 <div class="mxchat-card-header">
2551 <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2552 </div>
2553
2554 <div class="mxchat-intent-documentation">
2555 <p>
2556 <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2557 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2558 <?php esc_html_e('documentation', 'mxchat'); ?>
2559 </a>
2560 <?php esc_html_e(' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat'); ?>
2561 </p>
2562 </div>
2563
2564 <form id="mxchat-add-intent-form" method="post"
2565 action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2566 <input type="hidden" name="action" value="mxchat_add_intent">
2567 <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2568
2569 <div class="mxchat-form-group">
2570 <label for="intent_label">
2571 <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2572 </label>
2573 <input name="intent_label" type="text" id="intent_label" required
2574 class="mxchat-intent-input"
2575 placeholder="<?php esc_attr_e('Example Email Capture: Newsletter Signup', 'mxchat'); ?>">
2576 </div>
2577
2578 <div class="mxchat-form-group">
2579 <label for="phrases">
2580 <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2581 </label>
2582 <textarea name="phrases" id="phrases" rows="5" required
2583 class="mxchat-intent-textarea"
2584 placeholder="<?php esc_attr_e('Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed', 'mxchat'); ?>"></textarea>
2585 </div>
2586
2587 <div class="mxchat-form-group">
2588 <label for="callback_function">
2589 <?php esc_html_e('Callback Function', 'mxchat'); ?>
2590 </label>
2591 <select name="callback_function" id="callback_function"
2592 class="mxchat-intent-select" required>
2593 <option value="">
2594 <?php esc_html_e('Select a Callback', 'mxchat'); ?>
2595 </option>
2596 <?php
2597 $groups = $this->mxchat_get_available_callbacks(true);
2598 foreach ($groups as $group_label => $group_callbacks) :
2599 echo '<optgroup label="' . esc_attr($group_label) . '">';
2600 foreach ($group_callbacks as $function => $data) :
2601 $label = $data['label'];
2602 $pro_only = $data['pro_only'];
2603 $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2604 $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2605 ?>
2606 <option value="<?php echo esc_attr($function); ?>"
2607 <?php echo $disabled; ?>>
2608 <?php echo esc_html($label . $label_suffix); ?>
2609 </option>
2610 <?php
2611 endforeach;
2612 echo '</optgroup>';
2613 endforeach;
2614 ?>
2615 </select>
2616 </div>
2617
2618 <button type="submit" class="mxchat-button-primary">
2619 <?php esc_html_e('Add Intent', 'mxchat'); ?>
2620 </button>
2621 </form>
2622 </div>
2623
2624 <!-- Manage Intents Card -->
2625 <div class="mxchat-card">
2626 <div class="mxchat-card-header">
2627 <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2628 <div class="mxchat-header-actions">
2629 <form method="get" class="mxchat-search-form">
2630 <input type="hidden" name="page" value="mxchat-intents">
2631 <div class="mxchat-search-group">
2632 <span class="dashicons dashicons-search"></span>
2633 <input type="text" name="s"
2634 placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2635 value="<?php echo esc_attr($search_term); ?>">
2636 <select name="callback_filter" class="mxchat-intent-filter">
2637 <option value="">
2638 <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2639 </option>
2640 <?php foreach ($available_callbacks as $function => $callback_data) :
2641 $label = $callback_data['label']; ?>
2642 <option value="<?php echo esc_attr($function); ?>"
2643 <?php selected($callback_filter, $function); ?>>
2644 <?php echo esc_html($label); ?>
2645 </option>
2646 <?php endforeach; ?>
2647 </select>
2648 <button type="submit" class="mxchat-button-secondary">
2649 <?php esc_html_e('Filter', 'mxchat'); ?>
2650 </button>
2651 </div>
2652 </form>
2653 </div>
2654 </div>
2655
2656 <div class="mxchat-table-wrapper">
2657 <table class="mxchat-records-table">
2658 <thead>
2659 <tr>
2660 <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2661 <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2662 <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2663 <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2664 <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2665 </tr>
2666 </thead>
2667 <tbody>
2668 <?php if ($intents) :
2669 foreach ($intents as $intent) :
2670 $callback_function = $intent->callback_function;
2671 $callback_label = isset($available_callbacks[$callback_function]['label'])
2672 ? $available_callbacks[$callback_function]['label']
2673 : $callback_function;
2674 $threshold_value = isset($intent->similarity_threshold)
2675 ? round($intent->similarity_threshold * 100)
2676 : 85;
2677 ?>
2678 <tr>
2679 <td><?php echo esc_html($intent->intent_label); ?></td>
2680 <td class="mxchat-content-cell">
2681 <?php echo esc_html($intent->phrases); ?>
2682 </td>
2683 <td><?php echo esc_html($callback_label); ?></td>
2684 <td>
2685 <form method="post"
2686 action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2687 class="mxchat-threshold-form">
2688 <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2689 <input type="hidden" name="action"
2690 value="mxchat_update_intent_threshold">
2691 <input type="hidden" name="intent_id"
2692 value="<?php echo esc_attr($intent->id); ?>">
2693 <div class="mxchat-slider-group">
2694 <input type="range"
2695 name="intent_threshold"
2696 id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2697 min="70"
2698 max="95"
2699 value="<?php echo esc_attr($threshold_value); ?>"
2700 class="mxchat-intent-slider"
2701 oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2702 <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2703 class="mxchat-intent-output">
2704 <?php echo esc_html($threshold_value); ?>%
2705 </output>
2706 </div>
2707 <button type="submit" class="mxchat-button-icon">
2708 <span class="dashicons dashicons-saved"></span>
2709 </button>
2710 </form>
2711 </td>
2712 <td class="mxchat-actions-cell">
2713 <button type="button"
2714 class="mxchat-button-icon mxchat-edit-button"
2715 data-intent-id="<?php echo esc_attr($intent->id); ?>"
2716 data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2717 <span class="dashicons dashicons-edit"></span>
2718 </button>
2719 <form method="post"
2720 action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2721 class="mxchat-delete-form"
2722 onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2723 <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2724 <input type="hidden" name="action" value="mxchat_delete_intent">
2725 <input type="hidden" name="intent_id"
2726 value="<?php echo esc_attr($intent->id); ?>">
2727 <button type="submit" class="mxchat-button-icon">
2728 <span class="dashicons dashicons-trash"></span>
2729 </button>
2730 </form>
2731 </td>
2732 </tr>
2733 <?php endforeach;
2734 else : ?>
2735 <tr>
2736 <td colspan="5" class="mxchat-no-records">
2737 <?php esc_html_e('No intents found.', 'mxchat'); ?>
2738 </td>
2739 </tr>
2740 <?php endif; ?>
2741 </tbody>
2742 </table>
2743 </div>
2744
2745 <?php if ($total_pages > 1) : ?>
2746 <div class="mxchat-pagination">
2747 <?php
2748 echo paginate_links(array(
2749 'base' => add_query_arg('paged', '%#%'),
2750 'format' => '',
2751 'prev_text' => __('&laquo; Previous', 'mxchat'),
2752 'next_text' => __('Next &raquo;', 'mxchat'),
2753 'total' => $total_pages,
2754 'current' => $page
2755 ));
2756 ?>
2757 </div>
2758 <?php endif; ?>
2759 </div>
2760 </div>
2761
2762 <!-- Edit Modal -->
2763 <div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
2764 <div class="mxchat-modal-content">
2765 <span class="mxchat-modal-close">&times;</span>
2766 <div class="mxchat-card-header">
2767 <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2768 </div>
2769
2770 <form id="mxchat-edit-intent-form" method="post"
2771 action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2772 <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2773 <input type="hidden" name="action" value="mxchat_edit_intent">
2774 <input type="hidden" name="intent_id" id="edit_intent_id">
2775
2776 <div class="mxchat-form-group">
2777 <label for="edit_phrases">
2778 <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2779 </label>
2780 <textarea name="phrases"
2781 id="edit_phrases"
2782 rows="5"
2783 class="mxchat-intent-textarea"
2784 required></textarea>
2785 </div>
2786
2787 <div class="mxchat-modal-actions">
2788 <button type="submit" class="mxchat-button-primary">
2789 <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2790 </button>
2791 <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2792 <?php esc_html_e('Cancel', 'mxchat'); ?>
2793 </button>
2794 </div>
2795 </form>
2796 </div>
2797 </div>
2798
2799 </div><!-- .mxchat-content -->
2800 </div><!-- .mxchat-wrapper -->
2801
2802 <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
2803 <div class="mxchat-intent-loading-spinner"></div>
2804 <div class="mxchat-intent-loading-text">
2805 <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
2806 </div>
2807 </div>
2808 <?php
2809 }
2810
2811
2812
2813 /**
2814 * Handle editing of intent phrases
2815 *
2816 * @since 1.0.0
2817 * @return void
2818 */
2819 public function handle_edit_intent() {
2820 // Verify nonce and user capabilities
2821 if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
2822 wp_die(esc_html__('Security check failed.', 'mxchat'));
2823 }
2824
2825 if (!current_user_can('manage_options')) {
2826 wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
2827 }
2828
2829 // Validate and sanitize input
2830 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2831 if (!$intent_id) {
2832 wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
2833 }
2834
2835 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2836 if (empty($phrases_input)) {
2837 wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
2838 }
2839
2840 // Process phrases the same way as in intent creation
2841 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2842
2843 if (empty($phrases_array)) {
2844 wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
2845 }
2846
2847 // Generate embeddings and combine them
2848 $vectors = [];
2849 foreach ($phrases_array as $phrase) {
2850 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2851 if (is_array($embedding_vector)) {
2852 $vectors[] = $embedding_vector;
2853 } else {
2854 wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
2855 }
2856 }
2857
2858 if (empty($vectors)) {
2859 wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
2860 }
2861
2862 // Create combined vector just like in intent creation
2863 $combined_vector = $this->mxchat_average_vectors($vectors);
2864 $serialized_vector = maybe_serialize($combined_vector);
2865
2866 global $wpdb;
2867 $table_name = $wpdb->prefix . 'mxchat_intents';
2868
2869 // Update the intent with both new phrases and the combined vector
2870 $result = $wpdb->update(
2871 $table_name,
2872 array(
2873 'phrases' => implode(', ', $phrases_array),
2874 'embedding_vector' => $serialized_vector
2875 ),
2876 array('id' => $intent_id),
2877 array('%s', '%s'),
2878 array('%d')
2879 );
2880
2881 if (false === $result) {
2882 wp_die(esc_html__('Failed to update intent.', 'mxchat'));
2883 }
2884
2885 // Redirect back to the intents page with a success message
2886 $redirect_url = add_query_arg(
2887 array(
2888 'page' => 'mxchat-intents',
2889 'updated' => 'true'
2890 ),
2891 admin_url('admin.php')
2892 );
2893
2894 wp_safe_redirect($redirect_url);
2895 exit;
2896 }
2897 public function mxchat_handle_update_intent_threshold() {
2898 if ( ! current_user_can( 'manage_options' ) ) {
2899 wp_die( esc_html__('Unauthorized user', 'mxchat') );
2900 }
2901
2902 check_admin_referer('mxchat_update_intent_threshold_nonce');
2903
2904 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
2905 global $wpdb;
2906 $table_name = $wpdb->prefix . 'mxchat_intents';
2907 $intent_id = intval($_POST['intent_id']);
2908 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
2909 $similarity_threshold = $threshold_percentage / 100;
2910
2911 $wpdb->update(
2912 $table_name,
2913 ['similarity_threshold' => $similarity_threshold],
2914 ['id' => $intent_id],
2915 ['%f'],
2916 ['%d']
2917 );
2918 }
2919
2920 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2921 exit;
2922 }
2923
2924 public function mxchat_handle_add_intent() {
2925 if ( ! current_user_can( 'manage_options' ) ) {
2926 wp_die( esc_html__('Unauthorized user', 'mxchat') );
2927 }
2928
2929 check_admin_referer('mxchat_add_intent_nonce');
2930
2931 global $wpdb;
2932 $table_name = $wpdb->prefix . 'mxchat_intents';
2933
2934 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
2935 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2936 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2937 $default_threshold = 0.85;
2938
2939 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
2940 wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
2941 }
2942
2943 $available_callbacks = $this->mxchat_get_available_callbacks();
2944
2945 if (!array_key_exists($callback_function, $available_callbacks)) {
2946 wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
2947 }
2948
2949 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2950 if ($is_pro_only && !$this->is_activated) {
2951 wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
2952 }
2953
2954 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2955
2956 if (empty($phrases_array)) {
2957 wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
2958 }
2959
2960 $vectors = [];
2961 foreach ($phrases_array as $phrase) {
2962 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2963 if (is_array($embedding_vector)) {
2964 $vectors[] = $embedding_vector;
2965 } else {
2966 wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
2967 }
2968 }
2969
2970 if (!empty($vectors)) {
2971 $combined_vector = $this->mxchat_average_vectors($vectors);
2972 $serialized_vector = maybe_serialize($combined_vector);
2973
2974 $result = $wpdb->insert($table_name, [
2975 'intent_label' => $intent_label,
2976 'phrases' => implode(', ', $phrases_array),
2977 'embedding_vector' => $serialized_vector,
2978 'callback_function' => $callback_function,
2979 'similarity_threshold' => $default_threshold,
2980 ]);
2981
2982 if ($result === false) {
2983 wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
2984 }
2985 } else {
2986 wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
2987 }
2988
2989 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2990 exit;
2991 }
2992
2993
2994
2995
2996
2997
2998 private function mxchat_get_available_callbacks($grouped = false) {
2999 $callbacks = [
3000 'mxchat_handle_email_capture' => [
3001 'label' => __('Email Capture', 'mxchat'),
3002 'pro_only' => false,
3003 'group' => __('Customer Engagement', 'mxchat'),
3004 ],
3005 'mxchat_handle_product_inquiry' => [
3006 'label' => __('Show Product Card', 'mxchat'),
3007 'pro_only' => true,
3008 'group' => __('WooCommerce Features', 'mxchat'),
3009 ],
3010 'mxchat_handle_order_history' => [
3011 'label' => __('Order History', 'mxchat'),
3012 'pro_only' => true,
3013 'group' => __('WooCommerce Features', 'mxchat'),
3014 ],
3015 'mxchat_generate_image' => [
3016 'label' => __('Generate Image', 'mxchat'),
3017 'pro_only' => true,
3018 'group' => __('Other Features', 'mxchat'),
3019 ],
3020 'mxchat_handle_search_request' => [
3021 'label' => __('Brave Web Search', 'mxchat'),
3022 'pro_only' => false,
3023 'group' => __('Search Features', 'mxchat'),
3024 ],
3025 'mxchat_handle_image_search_request' => [
3026 'label' => __('Brave Image Search', 'mxchat'),
3027 'pro_only' => false,
3028 'group' => __('Search Features', 'mxchat'),
3029 ],
3030 'mxchat_handle_add_to_cart_intent' => [
3031 'label' => __('Add to Cart', 'mxchat'),
3032 'pro_only' => true,
3033 'group' => __('WooCommerce Features', 'mxchat'),
3034 ],
3035 'mxchat_handle_checkout_intent' => [
3036 'label' => __('Proceed to Checkout', 'mxchat'),
3037 'pro_only' => true,
3038 'group' => __('WooCommerce Features', 'mxchat'),
3039 ],
3040 'mxchat_handle_pdf_discussion' => [
3041 'label' => __('Chat with PDF', 'mxchat'),
3042 'pro_only' => true,
3043 'group' => __('Other Features', 'mxchat'),
3044 ],
3045 'mxchat_handle_product_recommendations' => [
3046 'label' => __('Product Recommendations', 'mxchat'),
3047 'pro_only' => true,
3048 'group' => __('WooCommerce Features', 'mxchat'),
3049 ],
3050 'mxchat_live_agent_handover' => [
3051 'label' => __('Live Agent', 'mxchat'),
3052 'pro_only' => true,
3053 'group' => __('Customer Engagement', 'mxchat'),
3054 ],
3055 'mxchat_handle_switch_to_chatbot_intent' => [
3056 'label' => __('Back to Chatbot', 'mxchat'),
3057 'pro_only' => true,
3058 'group' => __('Customer Engagement', 'mxchat'),
3059 ],
3060 ];
3061
3062 // Return grouped structure if requested
3063 if ($grouped) {
3064 $grouped_callbacks = [];
3065 foreach ($callbacks as $key => $data) {
3066 $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3067 $grouped_callbacks[$group_label][$key] = [
3068 'label' => $data['label'],
3069 'pro_only' => $data['pro_only'],
3070 ];
3071 }
3072 return $grouped_callbacks;
3073 }
3074
3075 return $callbacks;
3076 }
3077
3078
3079
3080 private function mxchat_average_vectors($vectors) {
3081 $vector_length = count($vectors[0]);
3082 $sum_vector = array_fill(0, $vector_length, 0);
3083
3084 foreach ($vectors as $vector) {
3085 for ($i = 0; $i < $vector_length; $i++) {
3086 $sum_vector[$i] += $vector[$i];
3087 }
3088 }
3089
3090 // Divide each component by the number of vectors to get the average
3091 $num_vectors = count($vectors);
3092 for ($i = 0; $i < $vector_length; $i++) {
3093 $sum_vector[$i] /= $num_vectors;
3094 }
3095
3096 return $sum_vector;
3097 }
3098
3099 public function mxchat_handle_delete_intent() {
3100 if ( ! current_user_can( 'manage_options' ) ) {
3101 wp_die( esc_html__('Unauthorized user', 'mxchat') );
3102 }
3103
3104 check_admin_referer('mxchat_delete_intent_nonce');
3105
3106 if (isset($_POST['intent_id'])) {
3107 global $wpdb;
3108 $table_name = $wpdb->prefix . 'mxchat_intents';
3109 $intent_id = intval($_POST['intent_id']);
3110
3111 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3112 }
3113
3114 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3115 exit;
3116 }
3117
3118
3119 public function mxchat_page_init() {
3120 // Register settings
3121 register_setting(
3122 'mxchat_option_group',
3123 'mxchat_options',
3124 array($this, 'mxchat_sanitize')
3125 );
3126
3127 register_setting(
3128 'mxchat_option_group',
3129 'mxchat_similarity_threshold',
3130 array(
3131 'type' => 'number',
3132 'sanitize_callback' => function($value) {
3133 $value = absint($value); // Ensure it's an integer
3134 return min(max($value, 70), 85); // Enforce range
3135 },
3136 'default' => 80,
3137 )
3138 );
3139
3140 // Chatbot Settings Section
3141 add_settings_section(
3142 'mxchat_chatbot_section',
3143 esc_html__('Chatbot Settings', 'mxchat'),
3144 null,
3145 'mxchat-chatbot'
3146 );
3147
3148 // Similarity Threshold Slider
3149 add_settings_field(
3150 'similarity_threshold', // Field ID
3151 esc_html__('Similarity Threshold', 'mxchat'), // Field title
3152 array($this, 'mxchat_similarity_threshold_callback'), // Callback function
3153 'mxchat-chatbot', // Page
3154 'mxchat_chatbot_section' // Section
3155 );
3156
3157 add_settings_field(
3158 'append_to_body',
3159 esc_html__('Auto-Display Chatbot', 'mxchat'),
3160 array($this, 'mxchat_append_to_body_callback'),
3161 'mxchat-chatbot',
3162 'mxchat_chatbot_section'
3163 );
3164
3165
3166 // Existing fields...
3167 add_settings_field(
3168 'api_key',
3169 esc_html__('OpenAI API Key', 'mxchat'),
3170 array($this, 'api_key_callback'),
3171 'mxchat-chatbot',
3172 'mxchat_chatbot_section'
3173 );
3174
3175 add_settings_field(
3176 'xai_api_key',
3177 esc_html__('X.AI API Key', 'mxchat'),
3178 array($this, 'xai_api_key_callback'),
3179 'mxchat-chatbot',
3180 'mxchat_chatbot_section'
3181 );
3182
3183 add_settings_field(
3184 'claude_api_key',
3185 esc_html__('Claude API Key', 'mxchat'),
3186 array($this, 'claude_api_key_callback'),
3187 'mxchat-chatbot',
3188 'mxchat_chatbot_section'
3189 );
3190
3191 add_settings_field(
3192 'deepseek_api_key',
3193 esc_html__('DeepSeek API Key', 'mxchat'),
3194 array($this, 'deepseek_api_key_callback'),
3195 'mxchat-chatbot',
3196 'mxchat_chatbot_section'
3197 );
3198
3199 add_settings_field(
3200 'system_prompt_instructions',
3201 esc_html__('AI Instructions (Behavior)', 'mxchat'),
3202 array($this, 'system_prompt_instructions_callback'),
3203 'mxchat-chatbot',
3204 'mxchat_chatbot_section'
3205 );
3206
3207 add_settings_field(
3208 'model',
3209 esc_html__('Model', 'mxchat'),
3210 array($this, 'mxchat_model_callback'),
3211 'mxchat-chatbot',
3212 'mxchat_chatbot_section'
3213 );
3214
3215 add_settings_field(
3216 'top_bar_title',
3217 esc_html__('Top Bar Title', 'mxchat'),
3218 array($this, 'mxchat_top_bar_title_callback'),
3219 'mxchat-chatbot',
3220 'mxchat_chatbot_section'
3221 );
3222
3223 add_settings_field(
3224 'enable_email_block',
3225 esc_html__('Require Email To Chat', 'mxchat'),
3226 array($this, 'enable_email_block_callback'),
3227 'mxchat-chatbot',
3228 'mxchat_chatbot_section'
3229 );
3230
3231 add_settings_field(
3232 'email_blocker_header_content',
3233 esc_html__('Require Email Chat Content', 'mxchat'),
3234 array($this, 'email_blocker_header_content_callback'),
3235 'mxchat-chatbot',
3236 'mxchat_chatbot_section'
3237 );
3238
3239 add_settings_field(
3240 'email_blocker_button_text',
3241 esc_html__('Require Email Chat Button Text', 'mxchat'),
3242 [$this, 'email_blocker_button_text_callback'],
3243 'mxchat-chatbot',
3244 'mxchat_chatbot_section'
3245 );
3246
3247 add_settings_field(
3248 'intro_message',
3249 esc_html__('Introductory Message', 'mxchat'),
3250 array($this, 'mxchat_intro_message_callback'),
3251 'mxchat-chatbot',
3252 'mxchat_chatbot_section'
3253 );
3254
3255 add_settings_field(
3256 'input_copy',
3257 esc_html__('Input Copy', 'mxchat'),
3258 array($this, 'mxchat_input_copy_callback'),
3259 'mxchat-chatbot',
3260 'mxchat_chatbot_section'
3261 );
3262
3263 add_settings_field(
3264 'rate_limit_logged_out',
3265 __('Rate Limit for Logged-out Users', 'mxchat'),
3266 array($this, 'mxchat_rate_limit_logged_out_callback'),
3267 'mxchat-chatbot',
3268 'mxchat_chatbot_section'
3269 );
3270
3271 add_settings_field(
3272 'rate_limit_roles',
3273 __('Rate Limits by User Role', 'mxchat'),
3274 array($this, 'mxchat_rate_limit_roles_callback'),
3275 'mxchat-chatbot',
3276 'mxchat_chatbot_section'
3277 );
3278
3279 add_settings_field(
3280 'rate_limit_message',
3281 esc_html__('Rate Limit Message', 'mxchat'),
3282 array($this, 'mxchat_rate_limit_message_callback'),
3283 'mxchat-chatbot',
3284 'mxchat_chatbot_section'
3285 );
3286
3287 add_settings_field(
3288 'pre_chat_message',
3289 esc_html__('Chat Teaser Pop-up', 'mxchat'),
3290 array($this, 'mxchat_pre_chat_message_callback'),
3291 'mxchat-chatbot',
3292 'mxchat_chatbot_section'
3293 );
3294
3295 add_settings_field(
3296 'privacy_toggle',
3297 esc_html__('Toggle Privacy Notice', 'mxchat'),
3298 array($this, 'mxchat_privacy_toggle_callback'),
3299 'mxchat-chatbot',
3300 'mxchat_chatbot_section'
3301 );
3302
3303 add_settings_field(
3304 'complianz_toggle',
3305 esc_html__('Enable Complianz', 'mxchat'),
3306 array($this, 'mxchat_complianz_toggle_callback'),
3307 'mxchat-chatbot',
3308 'mxchat_chatbot_section'
3309 );
3310
3311 add_settings_field(
3312 'link_target_toggle',
3313 esc_html__('Open Links in a New Tab', 'mxchat'),
3314 array($this, 'mxchat_link_target_toggle_callback'),
3315 'mxchat-chatbot',
3316 'mxchat_chatbot_section'
3317 );
3318
3319 add_settings_field(
3320 'chat_persistence_toggle',
3321 esc_html__('Enable Chat Persistence', 'mxchat'),
3322 array($this, 'mxchat_chat_persistence_toggle_callback'),
3323 'mxchat-chatbot',
3324 'mxchat_chatbot_section'
3325 );
3326
3327 add_settings_field(
3328 'popular_question_1',
3329 esc_html__('Popular Question 1', 'mxchat'),
3330 array($this, 'mxchat_popular_question_1_callback'),
3331 'mxchat-chatbot',
3332 'mxchat_chatbot_section'
3333 );
3334
3335 add_settings_field(
3336 'popular_question_2',
3337 esc_html__('Popular Question 2', 'mxchat'),
3338 array($this, 'mxchat_popular_question_2_callback'),
3339 'mxchat-chatbot',
3340 'mxchat_chatbot_section'
3341 );
3342
3343 add_settings_field(
3344 'popular_question_3',
3345 esc_html__('Popular Question 3', 'mxchat'),
3346 array($this, 'mxchat_popular_question_3_callback'),
3347 'mxchat-chatbot',
3348 'mxchat_chatbot_section'
3349 );
3350
3351 add_settings_field(
3352 'additional_popular_questions',
3353 esc_html__('Additional Popular Questions', 'mxchat'),
3354 array($this, 'mxchat_additional_popular_questions_callback'),
3355 'mxchat-chatbot',
3356 'mxchat_chatbot_section'
3357 );
3358
3359 // WooCommerce Settings Section
3360 add_settings_section(
3361 'mxchat_woocommerce_section',
3362 esc_html__('WooCommerce Settings', 'mxchat'),
3363 null,
3364 'mxchat-embed'
3365 );
3366
3367 // Loops Settings Section
3368 add_settings_section(
3369 'mxchat_loops_section',
3370 esc_html__('Loops Settings', 'mxchat'),
3371 null,
3372 'mxchat-embed'
3373 );
3374
3375 // WooCommerce Settings Fields
3376 add_settings_field(
3377 'enable_woocommerce_integration',
3378 esc_html__('Automatically Embed Products', 'mxchat'),
3379 array($this, 'mxchat_enable_woocommerce_integration_callback'),
3380 'mxchat-embed',
3381 'mxchat_woocommerce_section'
3382 );
3383
3384 add_settings_field(
3385 'woocommerce_consumer_key',
3386 esc_html__('WooCommerce Consumer Key', 'mxchat'),
3387 array($this, 'mxchat_woocommerce_consumer_key_callback'),
3388 'mxchat-embed',
3389 'mxchat_woocommerce_section'
3390 );
3391
3392 add_settings_field(
3393 'woocommerce_consumer_secret',
3394 esc_html__('WooCommerce Consumer Secret', 'mxchat'),
3395 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
3396 'mxchat-embed',
3397 'mxchat_woocommerce_section'
3398 );
3399
3400 // Loops Settings Fields
3401 add_settings_field(
3402 'loops_api_key',
3403 esc_html__('Loops API Key', 'mxchat'),
3404 array($this, 'mxchat_loops_api_key_callback'),
3405 'mxchat-embed',
3406 'mxchat_loops_section'
3407 );
3408
3409 add_settings_field(
3410 'loops_mailing_list',
3411 esc_html__('Loops Mailing List', 'mxchat'),
3412 array($this, 'mxchat_loops_mailing_list_callback'),
3413 'mxchat-embed',
3414 'mxchat_loops_section'
3415 );
3416
3417 add_settings_field(
3418 'triggered_phrase_response',
3419 esc_html__('Triggered Phrase Response', 'mxchat'),
3420 array($this, 'mxchat_triggered_phrase_response_callback'),
3421 'mxchat-embed',
3422 'mxchat_loops_section'
3423 );
3424
3425 add_settings_field(
3426 'email_capture_response',
3427 esc_html__('Email Capture Response', 'mxchat'),
3428 array($this, 'mxchat_email_capture_response_callback'),
3429 'mxchat-embed',
3430 'mxchat_loops_section'
3431 );
3432
3433 // Brave Search Settings Fields
3434 add_settings_section(
3435 'mxchat_brave_section',
3436 __('Brave Search Settings', 'mxchat'),
3437 array($this, 'mxchat_brave_section_callback'),
3438 'mxchat-embed'
3439 );
3440
3441 add_settings_field(
3442 'brave_api_key',
3443 __('Brave API Key', 'mxchat'),
3444 array($this, 'mxchat_brave_api_key_callback'),
3445 'mxchat-embed',
3446 'mxchat_brave_section'
3447 );
3448
3449 add_settings_field(
3450 'brave_image_count',
3451 __('Number of Images to Return', 'mxchat'),
3452 array($this, 'mxchat_brave_image_count_callback'),
3453 'mxchat-embed',
3454 'mxchat_brave_section'
3455 );
3456
3457 add_settings_field(
3458 'brave_safe_search',
3459 __('Safe Search', 'mxchat'),
3460 array($this, 'mxchat_brave_safe_search_callback'),
3461 'mxchat-embed',
3462 'mxchat_brave_section'
3463 );
3464
3465 add_settings_field(
3466 'brave_news_count',
3467 __('Number of News Articles', 'mxchat'),
3468 array($this, 'mxchat_brave_news_count_callback'),
3469 'mxchat-embed',
3470 'mxchat_brave_section'
3471 );
3472
3473 add_settings_field(
3474 'brave_country',
3475 __('Country', 'mxchat'),
3476 array($this, 'mxchat_brave_country_callback'),
3477 'mxchat-embed',
3478 'mxchat_brave_section'
3479 );
3480
3481 add_settings_field(
3482 'brave_language',
3483 __('Language', 'mxchat'),
3484 array($this, 'mxchat_brave_language_callback'),
3485 'mxchat-embed',
3486 'mxchat_brave_section'
3487 );
3488
3489 // Chat with PDF Intent Settings Fields
3490 add_settings_section(
3491 'mxchat_pdf_intent_section',
3492 __('Toolbar Settings & Intents', 'mxchat'),
3493 array($this, 'mxchat_pdf_intent_section_callback'),
3494 'mxchat-embed'
3495 );
3496
3497 add_settings_field(
3498 'chat_toolbar_toggle',
3499 __('Show Chat Toolbar', 'mxchat'),
3500 array($this, 'mxchat_chat_toolbar_toggle_callback'),
3501 'mxchat-embed',
3502 'mxchat_pdf_intent_section'
3503 );
3504
3505 add_settings_field(
3506 'pdf_intent_trigger_text',
3507 __('Intent Trigger Text', 'mxchat'),
3508 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3509 'mxchat-embed',
3510 'mxchat_pdf_intent_section'
3511 );
3512
3513 add_settings_field(
3514 'pdf_intent_success_text',
3515 __('Success Text', 'mxchat'),
3516 array($this, 'mxchat_pdf_intent_success_text_callback'),
3517 'mxchat-embed',
3518 'mxchat_pdf_intent_section'
3519 );
3520
3521 add_settings_field(
3522 'pdf_intent_error_text',
3523 __('Error Text', 'mxchat'),
3524 array($this, 'mxchat_pdf_intent_error_text_callback'),
3525 'mxchat-embed',
3526 'mxchat_pdf_intent_section'
3527 );
3528
3529 // Add PDF Maximum Pages Field
3530 add_settings_field(
3531 'pdf_max_pages',
3532 __('Maximum Document Pages', 'mxchat'),
3533 array($this, 'mxchat_pdf_max_pages_callback'),
3534 'mxchat-embed',
3535 'mxchat_pdf_intent_section'
3536 );
3537
3538 // Live Agent Settings Fields
3539 add_settings_section(
3540 'mxchat_live_agent_section',
3541 __('Live Agent Settings', 'mxchat'),
3542 array($this, 'mxchat_live_agent_section_callback'),
3543 'mxchat-embed'
3544 );
3545
3546 // Live Agent Status Fields (add at top of live agent settings)
3547 add_settings_field(
3548 'live_agent_status',
3549 __('Live Agent Status', 'mxchat'),
3550 array($this, 'mxchat_live_agent_status_callback'),
3551 'mxchat-embed',
3552 'mxchat_live_agent_section'
3553 );
3554
3555 add_settings_field(
3556 'live_agent_notification_message',
3557 __('Notification Message', 'mxchat'),
3558 array($this, 'mxchat_live_agent_notification_message_callback'),
3559 'mxchat-embed',
3560 'mxchat_live_agent_section'
3561 );
3562
3563 add_settings_field(
3564 'live_agent_away_message',
3565 __('Away Message', 'mxchat'),
3566 array($this, 'mxchat_live_agent_away_message_callback'),
3567 'mxchat-embed',
3568 'mxchat_live_agent_section'
3569 );
3570
3571 add_settings_field(
3572 'live_agent_webhook_url',
3573 __('Slack Webhook URL', 'mxchat'),
3574 array($this, 'mxchat_live_agent_webhook_url_callback'),
3575 'mxchat-embed',
3576 'mxchat_live_agent_section'
3577 );
3578
3579 add_settings_field(
3580 'live_agent_secret_key',
3581 __('Slack Secret Key', 'mxchat'),
3582 array($this, 'mxchat_live_agent_secret_key_callback'),
3583 'mxchat-embed',
3584 'mxchat_live_agent_section'
3585 );
3586
3587 // Live Agent Integration Fields
3588 add_settings_field(
3589 'live_agent_bot_token',
3590 __('Slack Bot OAuth Token', 'mxchat'),
3591 array($this, 'mxchat_live_agent_bot_token_callback'),
3592 'mxchat-embed',
3593 'mxchat_live_agent_section'
3594 );
3595
3596
3597
3598
3599 // General Settings Section
3600 add_settings_section(
3601 'mxchat_general_section',
3602 esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
3603 null,
3604 'mxchat-general'
3605 );
3606 }
3607
3608 public function mxchat_prompts_page_init() {
3609 // Register all settings as a single array
3610 register_setting(
3611 'mxchat_prompts_options',
3612 'mxchat_prompts_options',
3613 array(
3614 'type' => 'array',
3615 'description' => __('MXChat Knowledge Base Settings', 'mxchat'),
3616 'default' => array(
3617 'mxchat_auto_sync_posts' => 0,
3618 'mxchat_auto_sync_pages' => 0,
3619 'mxchat_use_pinecone' => 0,
3620 'mxchat_pinecone_api_key' => '',
3621 'mxchat_pinecone_environment' => '',
3622 'mxchat_pinecone_index' => '',
3623 'mxchat_pinecone_host' => '', // Add this line
3624 ),
3625 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3626 )
3627 );
3628
3629 // Add settings saved message
3630 add_action('admin_notices', array($this, 'sync_settings_notice'));
3631 }
3632
3633 /**
3634 * Sanitize all prompts options
3635 *
3636 * @param array $input The unsanitized options array
3637 * @return array The sanitized options array
3638 */
3639 public function sanitize_prompts_options($input) {
3640 // Log the incoming input.
3641 //error_log('Sanitizing inputs: ' . print_r($input, true));
3642
3643 $sanitized = array();
3644
3645 // Boolean options
3646 $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0;
3647 $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0;
3648 $sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0;
3649
3650 // API Key: if less than 32 characters, flag as invalid.
3651 $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
3652 if (!empty($api_key) && strlen($api_key) < 32) {
3653 add_settings_error(
3654 'mxchat_prompts_options',
3655 'invalid_api_key',
3656 __('The Pinecone API key appears to be invalid. Please check your API key.', 'mxchat')
3657 );
3658 $existing_options = get_option('mxchat_prompts_options', array());
3659 $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? '';
3660 } else {
3661 $sanitized['mxchat_pinecone_api_key'] = $api_key;
3662 }
3663
3664 // Environment and Index Name
3665 $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
3666 $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
3667
3668 // Host: Remove protocol and validate format.
3669 $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
3670 $host = preg_replace('#^https?://#', '', $host);
3671 //error_log('Host after removing protocol: ' . $host);
3672 if (!empty($host)) {
3673 if (!preg_match('/^[\w-]+\.svc\.[\w-]+\.pinecone\.io$/', $host)) {
3674 add_settings_error(
3675 'mxchat_prompts_options',
3676 'invalid_host',
3677 __('The Pinecone host appears to be invalid. It should look like "mxchat-vectors-zrmsquq.svc.aped-4627-b74a.pinecone.io"', 'mxchat')
3678 );
3679 $existing_options = get_option('mxchat_prompts_options', array());
3680 $sanitized['mxchat_pinecone_host'] = $existing_options['mxchat_pinecone_host'] ?? '';
3681 } else {
3682 $sanitized['mxchat_pinecone_host'] = $host;
3683 }
3684 } else {
3685 $sanitized['mxchat_pinecone_host'] = '';
3686 }
3687
3688 //error_log('Final sanitized array: ' . print_r($sanitized, true));
3689
3690 return $sanitized;
3691 }
3692
3693
3694 public function sync_settings_notice() {
3695 // Only show notice on our plugin page
3696 if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3697 return;
3698 }
3699
3700 // Check if settings were updated
3701 if (isset($_GET['settings-updated'])) {
3702
3703 ?>
3704 <div class="notice notice-success is-dismissible">
3705 <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
3706 </div>
3707 <?php
3708
3709 }
3710 }
3711 // Add this sanitization function to your class
3712 public function sanitize_sync_setting($input) {
3713 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3714 }
3715
3716
3717
3718 public function mxchat_handle_activate_license() {
3719 // Check nonce
3720 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3721 wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
3722 return;
3723 }
3724
3725 // Verify user capabilities
3726 if (!current_user_can('manage_options')) {
3727 wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
3728 return;
3729 }
3730
3731 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3732 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
3733
3734 if (empty($license_key) || empty($customer_email)) {
3735 wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
3736 return;
3737 }
3738
3739 $product_id = 'MxChatPRO';
3740 $response = wp_remote_get(
3741 add_query_arg(
3742 array(
3743 'wc-api' => 'software-api',
3744 'request' => 'activation',
3745 'email' => $customer_email,
3746 'license_key' => $license_key,
3747 'product_id' => $product_id
3748 ),
3749 'http://mxchat.ai/'
3750 )
3751 );
3752
3753 if (is_wp_error($response)) {
3754 wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message());
3755 return;
3756 }
3757
3758 $body = wp_remote_retrieve_body($response);
3759 $data = json_decode($body);
3760
3761 if ($data && isset($data->activated) && $data->activated) {
3762 update_option('mxchat_license_status', 'active');
3763 update_option('mxchat_pro_email', $customer_email);
3764 update_option('mxchat_activation_key', $license_key);
3765 wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
3766 } else {
3767 $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
3768 update_option('mxchat_license_status', 'inactive');
3769 update_option('mxchat_license_error', $error_message);
3770 wp_send_json_error($error_message);
3771 }
3772 }
3773
3774
3775 public function mxchat_rate_limit_logged_out_callback() {
3776 // Load the entire 'mxchat_options' array
3777 $all_options = get_option('mxchat_options', []);
3778
3779 // Retrieve the saved rate limit or use the default value
3780 $default_rate_limit = '10';
3781 $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
3782
3783 // Define available rate limits
3784 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3785
3786 // Output the dropdown
3787 echo '<div class="pro-feature-wrapper active">';
3788 echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3789 foreach ($rate_limits as $limit) {
3790 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3791 }
3792 echo '</select>';
3793 echo '<p class="description">' . esc_html__('Set the maximum number of messages a logged-out user can send within a 24-hour period.', 'mxchat') . '</p>';
3794 echo '</div>';
3795 }
3796
3797 // Add this new callback function
3798 public function mxchat_rate_limit_roles_callback() {
3799 $all_options = get_option('mxchat_options', []);
3800 $roles = wp_roles()->get_names();
3801 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3802
3803 echo '<div class="pro-feature-wrapper active mxchat-autosave-section">';
3804
3805 foreach ($roles as $role_id => $role_name) {
3806 $default_rate_limit = '100';
3807 $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id])
3808 ? $all_options['role_rate_limits'][$role_id]
3809 : $default_rate_limit;
3810
3811 echo '<div style="margin-bottom: 10px;">';
3812 echo '<label style="display: inline-block; width: 150px;">' . esc_html($role_name) . ':</label>';
3813 echo '<select
3814 id="role_rate_limits_' . esc_attr($role_id) . '"
3815 name="mxchat_options[role_rate_limits][' . esc_attr($role_id) . ']"
3816 class="mxchat-autosave-field">';
3817 foreach ($rate_limits as $limit) {
3818 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3819 }
3820 echo '</select>';
3821 echo '</div>';
3822 }
3823
3824 echo '<p class="description">' . esc_html__('Set the maximum number of messages users in each role can send within a 24-hour period.', 'mxchat') . '</p>';
3825 echo '</div>';
3826 }
3827
3828 public function mxchat_rate_limit_message_callback() {
3829 // Load the entire 'mxchat_options' array
3830 $all_options = get_option('mxchat_options', []);
3831
3832 // Retrieve the saved message or use the default value
3833 $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
3834 $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
3835
3836 // Output the textarea
3837 echo '<div class="pro-feature-wrapper active">';
3838 printf(
3839 '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
3840 esc_textarea($rate_limit_message)
3841 );
3842 echo '<p class="description">' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '</p>';
3843 echo '</div>';
3844 }
3845
3846
3847 public function mxchat_enable_woocommerce_integration_callback() {
3848 // Load from mxchat_options array
3849 $options = get_option('mxchat_options', []);
3850
3851 // Get WooCommerce integration status with backwards compatibility
3852 $woo_integration = isset($options['enable_woocommerce_integration'])
3853 ? $options['enable_woocommerce_integration']
3854 : '';
3855
3856 // Support both old ('1') and new ('on') values
3857 $checked = ($woo_integration === 'on' || $woo_integration === '1') ? 'checked' : '';
3858
3859 // Check if the feature is activated (paid feature)
3860 $disabled = $this->is_activated ? '' : 'disabled';
3861 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3862
3863 echo '<div class="' . esc_attr($class) . '">';
3864
3865 echo '<label class="toggle-switch">';
3866 echo sprintf(
3867 '<input type="checkbox" id="enable_woocommerce_integration" name="enable_woocommerce_integration" value="on" %s %s />',
3868 esc_attr($checked),
3869 esc_attr($disabled)
3870 );
3871 echo '<span class="slider"></span>';
3872 echo '</label>';
3873
3874 echo '<p class="description">' . esc_html__('Automatically syncs new product additions, updates, and removals to the knowledge base. For existing products, submit your product sitemap in the Knowledge Base section to add them initially.', 'mxchat') . '</p>';
3875
3876 // Pro feature overlay for non-activated users
3877 if (!$this->is_activated) {
3878 echo '<div class="pro-feature-overlay">';
3879 echo '<a href="https://mxchat.ai/" target="_blank">';
3880 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
3881 echo '</a>';
3882 echo '</div>';
3883 }
3884
3885 echo '</div>';
3886 }
3887
3888
3889
3890
3891 private function mxchat_add_option_field($id, $title, $callback = '') {
3892 add_settings_field(
3893 $id,
3894 __($title, 'mxchat'),
3895 $callback ? array($this, $callback) : array($this, $id . '_callback'),
3896 'mxchat-max',
3897 'mxchat_setting_section_id',
3898 $id === 'model' ? ['label_for' => 'model'] : []
3899 );
3900 }
3901
3902 public function api_key_callback() {
3903 // Retrieve from your stored 'api_key' in the mxchat_options array
3904 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3905
3906 // Notice we changed the name to "api_key" (no array notation).
3907 echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
3908 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3909 echo '<p class="description">' . esc_html__('The OpenAI API key is required even when using other models as it is used for vector embedding functionality. You must add credits to your OpenAI billing account before use.', 'mxchat') . '</p>';
3910 }
3911 public function xai_api_key_callback() {
3912 // Check if the feature is activated (paid feature)
3913 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3914 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3915
3916 // Retrieve the X.AI API key value
3917 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3918
3919 // Render the input field for the X.AI API key
3920 echo '<div class="' . esc_attr($class) . '">';
3921 printf(
3922 '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
3923 $xaiApiKey,
3924 $disabled
3925 );
3926 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3927
3928 // If the feature is not activated, show the overlay with a "Pro Only" message
3929 if (!$this->is_activated) {
3930 echo '<div class="pro-feature-overlay">';
3931 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>';
3932 echo '</div>';
3933 }
3934
3935 echo '</div>';
3936 }
3937 public function claude_api_key_callback() {
3938 // Check if the feature is activated (paid feature)
3939 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3940 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3941
3942 // Retrieve the Claude API key value
3943 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3944
3945 // Render the input field for the Claude API key
3946 echo '<div class="' . esc_attr($class) . '">';
3947 printf(
3948 '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
3949 $claudeApiKey,
3950 $disabled
3951 );
3952 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3953
3954 // If the feature is not activated, show the overlay with a "Pro Only" message
3955 if (!$this->is_activated) {
3956 echo '<div class="pro-feature-overlay">';
3957 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>';
3958 echo '</div>';
3959 }
3960
3961 echo '</div>';
3962 }
3963 public function deepseek_api_key_callback() {
3964 // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
3965 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
3966
3967 // Notice we changed the name to "deepseek_api_key" (no array notation).
3968 echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" />';
3969 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3970 }
3971
3972
3973 public function mxchat_woocommerce_consumer_key_callback() {
3974 // Load the entire 'mxchat_options' array
3975 $all_options = get_option('mxchat_options', []);
3976
3977 // Retrieve the WooCommerce consumer key or set a default
3978 $consumer_key = isset($all_options['woocommerce_consumer_key']) ? $all_options['woocommerce_consumer_key'] : '';
3979
3980 // Check if the feature is activated (paid feature)
3981 $disabled = $this->is_activated ? '' : 'disabled';
3982 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3983
3984 echo '<div class="' . esc_attr($class) . '">';
3985
3986 // Render the input field
3987 printf(
3988 '<input type="text" id="woocommerce_consumer_key" name="woocommerce_consumer_key" value="%s" class="regular-text" %s />',
3989 esc_attr($consumer_key),
3990 esc_attr($disabled)
3991 );
3992
3993 // Description for the input field
3994 echo '<p class="description">' . esc_html__('Enter your WooCommerce consumer key for integration.', 'mxchat') . '</p>';
3995
3996 // Pro feature overlay for non-activated users
3997 if (!$this->is_activated) {
3998 echo '<div class="pro-feature-overlay">';
3999 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>';
4000 echo '</div>';
4001 }
4002
4003 echo '</div>';
4004 }
4005
4006 public function mxchat_woocommerce_consumer_secret_callback() {
4007 // Get value with fallback
4008 $consumer_secret = isset($this->options['woocommerce_consumer_secret'])
4009 ? esc_attr($this->options['woocommerce_consumer_secret'])
4010 : '';
4011
4012 // Check if the feature is activated (paid feature)
4013 $disabled = $this->is_activated ? '' : 'disabled';
4014 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4015
4016 echo '<div class="' . esc_attr($class) . '">';
4017
4018 // Output the password input
4019 echo sprintf(
4020 '<input type="password"
4021 id="woocommerce_consumer_secret"
4022 name="woocommerce_consumer_secret"
4023 value="%s"
4024 class="regular-text"
4025 %s />',
4026 $consumer_secret,
4027 esc_attr($disabled)
4028 );
4029
4030 // Add show/hide button
4031 echo '<button type="button" id="toggleWooCommerceSecretVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4032
4033 // Pro feature overlay
4034 if (!$this->is_activated) {
4035 echo '<div class="pro-feature-overlay">';
4036 echo '<a href="https://mxchat.ai/" target="_blank">';
4037 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4038 echo '</a>';
4039 echo '</div>';
4040 }
4041
4042 echo '</div>';
4043 }
4044 public function mxchat_loops_api_key_callback() {
4045 // Support both old and new format
4046 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4047
4048 echo '<div class="api-key-wrapper">';
4049 echo sprintf(
4050 '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
4051 $loops_api_key
4052 );
4053 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4054 echo '</div>';
4055 echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
4056 }
4057
4058 public function mxchat_loops_mailing_list_callback() {
4059 // Add error handling and type checking
4060 $loops_api_key = '';
4061 $selected_list = '';
4062
4063 // Safely get the API key
4064 if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
4065 $loops_api_key = $this->options['loops_api_key'];
4066 }
4067
4068 // Safely get the selected list
4069 if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
4070 $selected_list = $this->options['loops_mailing_list'];
4071 }
4072
4073 if (!empty($loops_api_key)) {
4074 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4075 if (is_array($lists) && !empty($lists)) {
4076 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
4077 foreach ($lists as $list) {
4078 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4079 echo sprintf(
4080 '<option value="%s" %s>%s</option>',
4081 esc_attr($list['id']),
4082 selected($selected_list, $list['id'], false),
4083 esc_html($list['name'])
4084 );
4085 }
4086 }
4087 echo '</select>';
4088 } else {
4089 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4090 }
4091 } else {
4092 echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
4093 }
4094 }
4095 public function mxchat_triggered_phrase_response_callback() {
4096 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4097 $triggered_response = isset($this->options['triggered_phrase_response'])
4098 ? $this->options['triggered_phrase_response']
4099 : $default_response;
4100
4101 echo sprintf(
4102 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4103 esc_textarea($triggered_response)
4104 );
4105 echo '<p class="description">' . esc_html__('Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.', 'mxchat') . '</p>';
4106 }
4107
4108 public function mxchat_email_capture_response_callback() {
4109 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4110 $email_capture_response = isset($this->options['email_capture_response'])
4111 ? $this->options['email_capture_response']
4112 : $default_response;
4113
4114 echo sprintf(
4115 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4116 esc_textarea($email_capture_response)
4117 );
4118 echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
4119 }
4120
4121 public function mxchat_pre_chat_message_callback() {
4122 // Load the entire 'mxchat_options' array
4123 $all_options = get_option('mxchat_options', []);
4124
4125 // Retrieve the saved message or use the default value
4126 $default_message = __('Hey there! Ask me anything!', 'mxchat');
4127 $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
4128
4129 // Output the textarea
4130 printf(
4131 '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
4132 esc_textarea($pre_chat_message)
4133 );
4134 echo '<p class="description">' . esc_html__('Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.', 'mxchat') . '</p>';
4135 }
4136
4137
4138
4139 // Callback for AI Instructions textarea
4140 public function system_prompt_instructions_callback() {
4141 // Retrieve the current value of the system prompt instructions
4142 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4143
4144 // Render the textarea field
4145 printf(
4146 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4147 $instructions
4148 );
4149
4150 // Provide a helpful description
4151 echo '<p class="description">' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '</p>';
4152 }
4153
4154
4155 public function mxchat_model_callback() {
4156 // Define available models grouped by provider
4157 $models = array(
4158 esc_html__('X.AI Models', 'mxchat') => array(
4159 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4160 'grok-2' => esc_html__('Grok 2', 'mxchat')
4161 ),
4162 esc_html__('DeepSeek Models', 'mxchat') => array(
4163 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4164 ),
4165 esc_html__('Claude Models', 'mxchat') => array(
4166 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
4167 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
4168 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4169 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4170 ),
4171 esc_html__('OpenAI Models', 'mxchat') => array(
4172 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4173 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4174 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4175 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4176 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4177 )
4178 );
4179
4180 // Retrieve the currently selected model from saved options
4181 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4182
4183 // Begin the select dropdown
4184 echo '<select id="model" name="model">'; // No array notation in name attribute
4185
4186 // Iterate over groups of models
4187 foreach ($models as $group_label => $group_models) {
4188 echo '<optgroup label="' . esc_attr($group_label) . '">';
4189
4190 foreach ($group_models as $model_value => $model_label) {
4191 // Disable Pro-only models for non-activated users
4192 $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4193 $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') : '';
4194
4195 // Output the option element
4196 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
4197 }
4198
4199 echo '</optgroup>';
4200 }
4201
4202 // Close the select dropdown
4203 echo '</select>';
4204
4205 // Add a description below the dropdown
4206 echo '<p class="description">' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
4207 }
4208
4209
4210
4211 public function mxchat_top_bar_title_callback() {
4212 // Retrieve the current value of the top bar title from saved options
4213 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4214
4215 // Render the input field
4216 echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
4217
4218 // Add a description
4219 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4220 }
4221
4222 public function enable_email_block_callback() {
4223 // Load full plugin options array
4224 $all_options = get_option('mxchat_options', []);
4225
4226 // Get the value, default to 'off'
4227 $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
4228
4229 // Check if it's 'on'
4230 $checked = ($enable_email_block === 'on') ? 'checked' : '';
4231
4232 echo '<label class="toggle-switch">';
4233 echo sprintf(
4234 '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
4235 esc_attr($checked)
4236 );
4237 echo '<span class="slider"></span>';
4238 echo '</label>';
4239 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>';
4240 }
4241
4242
4243
4244 public function email_blocker_header_content_callback() {
4245 // Load the entire 'mxchat_options' array
4246 $all_options = get_option('mxchat_options', []);
4247
4248 // Retrieve the saved content or default to empty
4249 $content = isset($all_options['email_blocker_header_content'])
4250 ? $all_options['email_blocker_header_content']
4251 : '';
4252
4253 // Render the textarea
4254 echo '<textarea
4255 id="email_blocker_header_content"
4256 name="email_blocker_header_content"
4257 rows="5"
4258 cols="70"
4259 >' . esc_textarea($content) . '</textarea>';
4260
4261 echo '<p class="description">';
4262 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');
4263 echo '</p>';
4264 }
4265
4266
4267 public function email_blocker_button_text_callback() {
4268 // Load the entire 'mxchat_options' array
4269 $all_options = get_option('mxchat_options', []);
4270
4271 // Retrieve the saved button text or default to empty
4272 $button_text = isset($all_options['email_blocker_button_text'])
4273 ? $all_options['email_blocker_button_text']
4274 : '';
4275
4276 // Use esc_attr to safely render the existing text
4277 echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
4278
4279 echo '<p class="description">';
4280 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4281 echo '</p>';
4282 }
4283
4284
4285
4286 public function mxchat_intro_message_callback() {
4287 // Load the entire 'mxchat_options' array
4288 $all_options = get_option('mxchat_options', []);
4289
4290 // Retrieve the saved intro message or use the default
4291 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4292 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4293
4294 // Output the textarea with the saved value
4295 ?>
4296 <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
4297 <p class="description">
4298 <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
4299 </p>
4300 <?php
4301 }
4302
4303
4304 public function mxchat_input_copy_callback() {
4305 // Load the entire 'mxchat_options' array
4306 $all_options = get_option('mxchat_options', []);
4307
4308 // Retrieve the saved input copy or use the default value
4309 $default_copy = __('How can I assist?', 'mxchat');
4310 $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
4311
4312 // Output the input field with the saved value
4313 printf(
4314 '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
4315 esc_attr($input_copy),
4316 esc_attr__('How can I assist?', 'mxchat')
4317 );
4318
4319 // Output the description
4320 echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
4321 }
4322
4323
4324
4325 public function mxchat_user_message_font_color_callback() {
4326 $disabled = $this->is_activated ? '' : 'disabled';
4327 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4328
4329 echo '<div class="' . esc_attr($class) . '">';
4330 echo sprintf(
4331 '<input type="text"
4332 id="user_message_font_color"
4333 name="user_message_font_color"
4334 value="%s"
4335 class="my-color-field"
4336 data-default-color="#ffffff"
4337 %s />',
4338 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
4339 esc_attr($disabled)
4340 );
4341
4342 if (!$this->is_activated) {
4343 echo '<div class="pro-feature-overlay">';
4344 echo '<a href="https://mxchat.ai/" target="_blank">';
4345 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4346 echo '</a>';
4347 echo '</div>';
4348 }
4349 echo '</div>';
4350 }
4351
4352 public function mxchat_bot_message_bg_color_callback() {
4353 $disabled = $this->is_activated ? '' : 'disabled';
4354 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4355
4356 echo '<div class="' . esc_attr($class) . '">';
4357 echo sprintf(
4358 '<input type="text"
4359 id="bot_message_bg_color"
4360 name="bot_message_bg_color"
4361 value="%s"
4362 class="my-color-field"
4363 data-default-color="#e1e1e1"
4364 %s />',
4365 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
4366 esc_attr($disabled)
4367 );
4368
4369 if (!$this->is_activated) {
4370 echo '<div class="pro-feature-overlay">';
4371 echo '<a href="https://mxchat.ai/" target="_blank">';
4372 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4373 echo '</a>';
4374 echo '</div>';
4375 }
4376 echo '</div>';
4377 }
4378
4379 public function mxchat_bot_message_font_color_callback() {
4380 $disabled = $this->is_activated ? '' : 'disabled';
4381 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4382
4383 echo '<div class="' . esc_attr($class) . '">';
4384 echo sprintf(
4385 '<input type="text"
4386 id="bot_message_font_color"
4387 name="bot_message_font_color"
4388 value="%s"
4389 class="my-color-field"
4390 data-default-color="#333333"
4391 %s />',
4392 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
4393 esc_attr($disabled)
4394 );
4395
4396 if (!$this->is_activated) {
4397 echo '<div class="pro-feature-overlay">';
4398 echo '<a href="https://mxchat.ai/" target="_blank">';
4399 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4400 echo '</a>';
4401 echo '</div>';
4402 }
4403 echo '</div>';
4404 }
4405
4406 public function mxchat_live_agent_message_bg_color_callback() {
4407 $disabled = $this->is_activated ? '' : 'disabled';
4408 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4409
4410 echo '<div class="' . esc_attr($class) . '">';
4411 echo sprintf(
4412 '<input type="text"
4413 id="live_agent_message_bg_color"
4414 name="live_agent_message_bg_color"
4415 value="%s"
4416 class="my-color-field"
4417 data-default-color="#ffffff"
4418 %s />',
4419 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
4420 esc_attr($disabled)
4421 );
4422
4423 if (!$this->is_activated) {
4424 echo '<div class="pro-feature-overlay">';
4425 echo '<a href="https://mxchat.ai/" target="_blank">';
4426 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4427 echo '</a>';
4428 echo '</div>';
4429 }
4430 echo '</div>';
4431 }
4432
4433 public function mxchat_live_agent_message_font_color_callback() {
4434 $disabled = $this->is_activated ? '' : 'disabled';
4435 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4436
4437 echo '<div class="' . esc_attr($class) . '">';
4438 echo sprintf(
4439 '<input type="text"
4440 id="live_agent_message_font_color"
4441 name="live_agent_message_font_color"
4442 value="%s"
4443 class="my-color-field"
4444 data-default-color="#333333"
4445 %s />',
4446 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
4447 esc_attr($disabled)
4448 );
4449
4450 if (!$this->is_activated) {
4451 echo '<div class="pro-feature-overlay">';
4452 echo '<a href="https://mxchat.ai/" target="_blank">';
4453 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4454 echo '</a>';
4455 echo '</div>';
4456 }
4457 echo '</div>';
4458 }
4459
4460 public function mxchat_mode_indicator_bg_color_callback() {
4461 $disabled = $this->is_activated ? '' : 'disabled';
4462 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4463
4464 echo '<div class="' . esc_attr($class) . '">';
4465 echo sprintf(
4466 '<input type="text"
4467 id="mode_indicator_bg_color"
4468 name="mode_indicator_bg_color"
4469 value="%s"
4470 class="my-color-field"
4471 data-default-color="#767676"
4472 %s />',
4473 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
4474 esc_attr($disabled)
4475 );
4476
4477 if (!$this->is_activated) {
4478 echo '<div class="pro-feature-overlay">';
4479 echo '<a href="https://mxchat.ai/" target="_blank">';
4480 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4481 echo '</a>';
4482 echo '</div>';
4483 }
4484 echo '</div>';
4485 }
4486
4487 public function mxchat_mode_indicator_font_color_callback() {
4488 $disabled = $this->is_activated ? '' : 'disabled';
4489 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4490
4491 echo '<div class="' . esc_attr($class) . '">';
4492 echo sprintf(
4493 '<input type="text"
4494 id="mode_indicator_font_color"
4495 name="mode_indicator_font_color"
4496 value="%s"
4497 class="my-color-field"
4498 data-default-color="#ffffff"
4499 %s />',
4500 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
4501 esc_attr($disabled)
4502 );
4503
4504 if (!$this->is_activated) {
4505 echo '<div class="pro-feature-overlay">';
4506 echo '<a href="https://mxchat.ai/" target="_blank">';
4507 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4508 echo '</a>';
4509 echo '</div>';
4510 }
4511 echo '</div>';
4512 }
4513
4514 public function mxchat_toolbar_icon_color_callback() {
4515 $disabled = $this->is_activated ? '' : 'disabled';
4516 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4517
4518 echo '<div class="' . esc_attr($class) . '">';
4519 echo sprintf(
4520 '<input type="text"
4521 id="toolbar_icon_color"
4522 name="toolbar_icon_color"
4523 value="%s"
4524 class="my-color-field"
4525 data-default-color="#212121"
4526 %s />',
4527 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
4528 esc_attr($disabled)
4529 );
4530
4531 if (!$this->is_activated) {
4532 echo '<div class="pro-feature-overlay">';
4533 echo '<a href="https://mxchat.ai/" target="_blank">';
4534 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4535 echo '</a>';
4536 echo '</div>';
4537 }
4538 echo '</div>';
4539 }
4540
4541 public function mxchat_top_bar_bg_color_callback() {
4542 $disabled = $this->is_activated ? '' : 'disabled';
4543 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4544
4545 echo '<div class="' . esc_attr($class) . '">';
4546 echo sprintf(
4547 '<input type="text"
4548 id="top_bar_bg_color"
4549 name="top_bar_bg_color"
4550 value="%s"
4551 class="my-color-field"
4552 data-default-color="#00b294"
4553 %s />',
4554 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
4555 esc_attr($disabled)
4556 );
4557
4558 if (!$this->is_activated) {
4559 echo '<div class="pro-feature-overlay">';
4560 echo '<a href="https://mxchat.ai/" target="_blank">';
4561 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4562 echo '</a>';
4563 echo '</div>';
4564 }
4565 echo '</div>';
4566 }
4567
4568 public function mxchat_send_button_font_color_callback() {
4569 $disabled = $this->is_activated ? '' : 'disabled';
4570 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4571
4572 echo '<div class="' . esc_attr($class) . '">';
4573 echo sprintf(
4574 '<input type="text"
4575 id="send_button_font_color"
4576 name="send_button_font_color"
4577 value="%s"
4578 class="my-color-field"
4579 data-default-color="#ffffff"
4580 %s />',
4581 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
4582 esc_attr($disabled)
4583 );
4584
4585 if (!$this->is_activated) {
4586 echo '<div class="pro-feature-overlay">';
4587 echo '<a href="https://mxchat.ai/" target="_blank">';
4588 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4589 echo '</a>';
4590 echo '</div>';
4591 }
4592 echo '</div>';
4593 }
4594
4595 public function mxchat_chatbot_background_color_callback() {
4596 $disabled = $this->is_activated ? '' : 'disabled';
4597 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4598
4599 echo '<div class="' . esc_attr($class) . '">';
4600 echo sprintf(
4601 '<input type="text"
4602 id="chatbot_background_color"
4603 name="chatbot_background_color"
4604 value="%s"
4605 class="my-color-field"
4606 data-default-color="#000000"
4607 %s />',
4608 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
4609 esc_attr($disabled)
4610 );
4611
4612 if (!$this->is_activated) {
4613 echo '<div class="pro-feature-overlay">';
4614 echo '<a href="https://mxchat.ai/" target="_blank">';
4615 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4616 echo '</a>';
4617 echo '</div>';
4618 }
4619 echo '</div>';
4620 }
4621
4622 public function mxchat_icon_color_callback() {
4623 $disabled = $this->is_activated ? '' : 'disabled';
4624 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4625
4626 echo '<div class="' . esc_attr($class) . '">';
4627 echo sprintf(
4628 '<input type="text"
4629 id="icon_color"
4630 name="icon_color"
4631 value="%s"
4632 class="my-color-field"
4633 data-default-color="#ffffff"
4634 %s />',
4635 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
4636 esc_attr($disabled)
4637 );
4638
4639 if (!$this->is_activated) {
4640 echo '<div class="pro-feature-overlay">';
4641 echo '<a href="https://mxchat.ai/" target="_blank">';
4642 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4643 echo '</a>';
4644 echo '</div>';
4645 }
4646 echo '</div>';
4647 }
4648
4649 public function mxchat_custom_icon_callback() {
4650 $disabled = $this->is_activated ? '' : 'disabled';
4651 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4652 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
4653
4654 echo '<div class="' . esc_attr($class) . '">';
4655 echo sprintf(
4656 '<input type="url"
4657 id="custom_icon"
4658 name="custom_icon"
4659 value="%s"
4660 placeholder="%s"
4661 class="regular-text"
4662 %s />',
4663 $custom_icon_url,
4664 esc_attr__('Enter PNG URL', 'mxchat'),
4665 esc_attr($disabled)
4666 );
4667
4668 // Preview container for the icon
4669 if (!empty($custom_icon_url)) {
4670 echo '<div class="icon-preview" style="margin-top: 10px;">';
4671 echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4672 echo '</div>';
4673 }
4674
4675 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4676
4677 if (!$this->is_activated) {
4678 echo '<div class="pro-feature-overlay">';
4679 echo '<a href="https://mxchat.ai/" target="_blank">';
4680 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4681 echo '</a>';
4682 echo '</div>';
4683 }
4684 echo '</div>';
4685 }
4686
4687 public function mxchat_title_icon_callback() {
4688 $disabled = $this->is_activated ? '' : 'disabled';
4689 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4690 // Fixed the variable reference - it was using custom_icon instead of title_icon
4691 $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
4692
4693 echo '<div class="' . esc_attr($class) . '">';
4694 echo sprintf(
4695 '<input type="url"
4696 id="title_icon"
4697 name="title_icon"
4698 value="%s"
4699 placeholder="%s"
4700 class="regular-text"
4701 %s />',
4702 $title_icon_url,
4703 esc_attr__('Enter PNG URL', 'mxchat'),
4704 esc_attr($disabled)
4705 );
4706
4707 // Preview container for the icon
4708 if (!empty($title_icon_url)) {
4709 echo '<div class="icon-preview" style="margin-top: 10px;">';
4710 echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4711 echo '</div>';
4712 }
4713
4714 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4715
4716 if (!$this->is_activated) {
4717 echo '<div class="pro-feature-overlay">';
4718 echo '<a href="https://mxchat.ai/" target="_blank">';
4719 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4720 echo '</a>';
4721 echo '</div>';
4722 }
4723 echo '</div>';
4724 }
4725
4726 public function mxchat_chat_input_font_color_callback() {
4727 $disabled = $this->is_activated ? '' : 'disabled';
4728 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4729
4730 echo '<div class="' . esc_attr($class) . '">';
4731 echo sprintf(
4732 '<input type="text"
4733 id="chat_input_font_color"
4734 name="chat_input_font_color"
4735 value="%s"
4736 class="my-color-field"
4737 data-default-color="#555555"
4738 %s />',
4739 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
4740 esc_attr($disabled)
4741 );
4742
4743 if (!$this->is_activated) {
4744 echo '<div class="pro-feature-overlay">';
4745 echo '<a href="https://mxchat.ai/" target="_blank">';
4746 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4747 echo '</a>';
4748 echo '</div>';
4749 }
4750 echo '</div>';
4751 }
4752
4753 public function mxchat_append_to_body_callback() {
4754 // Get value from options array, default to 'off'
4755 $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
4756 $checked = ($append_to_body === 'on') ? 'checked' : '';
4757
4758 echo '<label class="toggle-switch">';
4759 echo sprintf(
4760 '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
4761 esc_attr($checked)
4762 );
4763 echo '<span class="slider"></span>';
4764 echo '</label>';
4765 echo '<p class="description">' .
4766 esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
4767 '</p>';
4768
4769 }
4770
4771
4772
4773 public function mxchat_privacy_toggle_callback() {
4774 // Load from mxchat_options array
4775 $options = get_option('mxchat_options', []);
4776
4777 // Get privacy toggle value with fallback
4778 $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
4779 $checked = ($privacy_toggle === 'on') ? 'checked' : '';
4780
4781 // Get privacy text with fallback
4782 $privacy_text = isset($options['privacy_text'])
4783 ? $options['privacy_text']
4784 : __('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat');
4785
4786 // Output the toggle switch
4787 echo '<label class="toggle-switch">';
4788 echo sprintf(
4789 '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
4790 esc_attr($checked)
4791 );
4792 echo '<span class="slider"></span>';
4793 echo '</label>';
4794 echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
4795
4796 // Output the custom text input field
4797 echo sprintf(
4798 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
4799 esc_textarea($privacy_text)
4800 );
4801 echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
4802 }
4803
4804
4805 public function mxchat_complianz_toggle_callback() {
4806 // Load from mxchat_options array
4807 $options = get_option('mxchat_options', []);
4808
4809 // Get complianz toggle value with fallback
4810 $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
4811 $checked = ($complianz_toggle === 'on') ? 'checked' : '';
4812
4813 // Output the toggle switch
4814 echo '<label class="toggle-switch">';
4815 echo sprintf(
4816 '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
4817 esc_attr($checked)
4818 );
4819 echo '<span class="slider"></span>';
4820 echo '</label>';
4821
4822 echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
4823 }
4824
4825 public function mxchat_link_target_toggle_callback() {
4826 // Load from mxchat_options array
4827 $options = get_option('mxchat_options', []);
4828
4829 // Get link target toggle value with fallback
4830 $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
4831 $checked = ($link_target_toggle === 'on') ? 'checked' : '';
4832
4833 // Output the toggle switch
4834 echo '<label class="toggle-switch">';
4835 echo sprintf(
4836 '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
4837 esc_attr($checked)
4838 );
4839 echo '<span class="slider"></span>';
4840 echo '</label>';
4841 echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
4842 }
4843
4844 public function mxchat_chat_persistence_toggle_callback() {
4845 // Load from mxchat_options array
4846 $options = get_option('mxchat_options', []);
4847
4848 // Get chat persistence toggle value with fallback
4849 $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
4850 $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
4851
4852 // Output the toggle switch
4853 echo '<label class="toggle-switch">';
4854 echo sprintf(
4855 '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
4856 esc_attr($checked)
4857 );
4858 echo '<span class="slider"></span>';
4859 echo '</label>';
4860
4861 echo '<p class="description">' . esc_html__('Enable to keep chat history when users navigate tabs or return to the site within 24 hours.', 'mxchat') . '</p>';
4862 }
4863
4864 public function mxchat_popular_question_1_callback() {
4865 // Load the full plugin options array
4866 $all_options = get_option('mxchat_options', []);
4867
4868 // Retrieve the specific option for popular_question_1
4869 $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
4870
4871 // Render the input field
4872 printf(
4873 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4874 esc_attr($popular_question_1),
4875 esc_attr__('Enter Popular Question 1', 'mxchat')
4876 );
4877
4878 // Add a description for the field
4879 echo '<p class="description">' . esc_html__('This will be the first popular question in the chatbot.', 'mxchat') . '</p>';
4880 }
4881
4882
4883 public function mxchat_popular_question_2_callback() {
4884 // Load the full plugin options array
4885 $all_options = get_option('mxchat_options', []);
4886
4887 // Retrieve the specific option for popular_question_2
4888 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
4889
4890 // Render the input field
4891 printf(
4892 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4893 esc_attr($popular_question_2),
4894 esc_attr__('Enter Popular Question 2', 'mxchat')
4895 );
4896
4897 // Add a description for the field
4898 echo '<p class="description">' . esc_html__('This will be the second popular question in the chatbot.', 'mxchat') . '</p>';
4899 }
4900
4901
4902 public function mxchat_popular_question_3_callback() {
4903 // Load the full plugin options array
4904 $all_options = get_option('mxchat_options', []);
4905
4906 // Retrieve the specific option for popular_question_3
4907 $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
4908
4909 // Render the input field
4910 printf(
4911 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
4912 esc_attr($popular_question_3),
4913 esc_attr(__('Enter Popular Question 3', 'mxchat'))
4914 );
4915
4916 // Add a description for the field
4917 echo '<p class="description">' . esc_html__('This will be the third popular question in the chatbot.', 'mxchat') . '</p>';
4918 }
4919
4920 public function mxchat_additional_popular_questions_callback() {
4921 $options = get_option('mxchat_options', []);
4922 $additional_questions = isset($options['additional_popular_questions'])
4923 ? $options['additional_popular_questions']
4924 : get_option('additional_popular_questions', array());
4925
4926 echo '<div id="mxchat-additional-questions-container">';
4927 if (!empty($additional_questions)) {
4928 foreach ($additional_questions as $index => $question) {
4929 printf(
4930 '<div class="mxchat-question-row">
4931 <input type="text" name="additional_popular_questions[]"
4932 value="%s"
4933 placeholder="%s"
4934 class="regular-text mxchat-question-input"
4935 data-question-index="%d" />
4936 <button type="button" class="button mxchat-remove-question"
4937 aria-label="%s">%s</button>
4938 </div>',
4939 esc_attr($question),
4940 esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
4941 $index,
4942 esc_attr(__('Remove question', 'mxchat')),
4943 esc_html__('Remove', 'mxchat')
4944 );
4945 }
4946 } else {
4947 printf(
4948 '<div class="mxchat-question-row">
4949 <input type="text" name="additional_popular_questions[]"
4950 value=""
4951 placeholder="%s"
4952 class="regular-text mxchat-question-input"
4953 data-question-index="0" />
4954 <button type="button" class="button mxchat-remove-question"
4955 aria-label="%s">%s</button>
4956 </div>',
4957 esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
4958 esc_attr(__('Remove question', 'mxchat')),
4959 esc_html__('Remove', 'mxchat')
4960 );
4961 }
4962 echo '</div>';
4963 printf(
4964 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
4965 esc_attr(__('Add question', 'mxchat')),
4966 esc_html__('Add Question', 'mxchat')
4967 );
4968 echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
4969 echo '</div>';
4970 }
4971
4972 public function mxchat_brave_api_key_callback() {
4973 $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
4974
4975 echo '<div class="api-key-wrapper">';
4976 echo sprintf(
4977 '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
4978 $brave_api_key
4979 );
4980 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4981 echo '</div>';
4982 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
4983 }
4984
4985 public function mxchat_brave_image_count_callback() {
4986 $brave_image_count = isset($this->options['brave_image_count'])
4987 ? intval($this->options['brave_image_count'])
4988 : 4;
4989
4990 echo sprintf(
4991 '<input type="number" id="brave_image_count" name="brave_image_count"
4992 value="%d" min="1" max="6" class="small-text" />',
4993 $brave_image_count
4994 );
4995 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
4996 }
4997
4998 public function mxchat_brave_safe_search_callback() {
4999 $brave_safe_search = isset($this->options['brave_safe_search'])
5000 ? esc_attr($this->options['brave_safe_search'])
5001 : 'strict';
5002
5003 echo '<select id="brave_safe_search" name="brave_safe_search">';
5004 echo sprintf(
5005 '<option value="strict" %s>%s</option>',
5006 selected($brave_safe_search, 'strict', false),
5007 __('Strict', 'mxchat')
5008 );
5009 echo sprintf(
5010 '<option value="off" %s>%s</option>',
5011 selected($brave_safe_search, 'off', false),
5012 __('Off', 'mxchat')
5013 );
5014 echo '</select>';
5015 echo '<p class="description">' .
5016 esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
5017 '</p>';
5018 }
5019
5020 public function mxchat_brave_news_count_callback() {
5021 $brave_news_count = isset($this->options['brave_news_count'])
5022 ? intval($this->options['brave_news_count'])
5023 : 3;
5024
5025 echo sprintf(
5026 '<input type="number" id="brave_news_count" name="brave_news_count"
5027 value="%d" min="1" max="10" class="small-text" />',
5028 $brave_news_count
5029 );
5030 echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
5031 }
5032
5033 public function mxchat_brave_country_callback() {
5034 $brave_country = isset($this->options['brave_country'])
5035 ? esc_attr($this->options['brave_country'])
5036 : 'us';
5037
5038 echo sprintf(
5039 '<input type="text" id="brave_country" name="brave_country"
5040 value="%s" maxlength="2" class="small-text" />',
5041 $brave_country
5042 );
5043 echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
5044 }
5045
5046 public function mxchat_brave_language_callback() {
5047 $brave_language = isset($this->options['brave_language'])
5048 ? esc_attr($this->options['brave_language'])
5049 : 'en';
5050
5051 echo sprintf(
5052 '<input type="text" id="brave_language" name="brave_language"
5053 value="%s" maxlength="2" class="small-text" />',
5054 $brave_language
5055 );
5056 echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
5057 }
5058
5059
5060
5061
5062
5063 // Section Callback
5064 public function mxchat_pdf_intent_section_callback() {
5065 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5066 }
5067
5068 public function mxchat_chat_toolbar_toggle_callback() {
5069 // Get chat toolbar toggle value with fallback
5070 $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5071 $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5072
5073 // Check if the plugin is activated (paid feature)
5074 $disabled = $this->is_activated ? '' : 'disabled';
5075 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5076
5077 echo '<div class="' . esc_attr($class) . '">';
5078
5079 // Output the toggle switch
5080 echo '<label class="toggle-switch">';
5081 echo sprintf(
5082 '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
5083 esc_attr($checked),
5084 esc_attr($disabled)
5085 );
5086 echo '<span class="slider"></span>';
5087 echo '</label>';
5088
5089 // Pro feature overlay
5090 if (!$this->is_activated) {
5091 echo '<div class="pro-feature-overlay">';
5092 echo '<a href="https://mxchat.ai/" target="_blank">';
5093 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5094 echo '</a>';
5095 echo '</div>';
5096 }
5097
5098 echo '</div>';
5099 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>';
5100 }
5101
5102
5103 public function mxchat_pdf_intent_trigger_text_callback() {
5104 $disabled = $this->is_activated ? '' : 'disabled';
5105 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5106 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5107
5108 echo '<div class="' . esc_attr($class) . '">';
5109 echo sprintf(
5110 '<textarea id="pdf_intent_trigger_text"
5111 name="pdf_intent_trigger_text"
5112 rows="3"
5113 cols="50"
5114 placeholder="%s"
5115 %s>%s</textarea>',
5116 esc_attr__('Enter trigger text', 'mxchat'),
5117 esc_attr($disabled),
5118 isset($this->options['pdf_intent_trigger_text'])
5119 ? esc_textarea($this->options['pdf_intent_trigger_text'])
5120 : esc_textarea($default_text)
5121 );
5122 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5123
5124 if (!$this->is_activated) {
5125 echo '<div class="pro-feature-overlay">';
5126 echo '<a href="https://mxchat.ai/" target="_blank">';
5127 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5128 echo '</a>';
5129 echo '</div>';
5130 }
5131 echo '</div>';
5132 }
5133
5134 public function mxchat_pdf_intent_success_text_callback() {
5135 $disabled = $this->is_activated ? '' : 'disabled';
5136 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5137 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5138
5139 echo '<div class="' . esc_attr($class) . '">';
5140 echo sprintf(
5141 '<textarea id="pdf_intent_success_text"
5142 name="pdf_intent_success_text"
5143 rows="3"
5144 cols="50"
5145 placeholder="%s"
5146 %s>%s</textarea>',
5147 esc_attr__('Enter success text', 'mxchat'),
5148 esc_attr($disabled),
5149 isset($this->options['pdf_intent_success_text'])
5150 ? esc_textarea($this->options['pdf_intent_success_text'])
5151 : esc_textarea($default_text)
5152 );
5153 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5154
5155 if (!$this->is_activated) {
5156 echo '<div class="pro-feature-overlay">';
5157 echo '<a href="https://mxchat.ai/" target="_blank">';
5158 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5159 echo '</a>';
5160 echo '</div>';
5161 }
5162 echo '</div>';
5163 }
5164
5165 public function mxchat_pdf_intent_error_text_callback() {
5166 $disabled = $this->is_activated ? '' : 'disabled';
5167 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5168 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5169
5170 echo '<div class="' . esc_attr($class) . '">';
5171 echo sprintf(
5172 '<textarea id="pdf_intent_error_text"
5173 name="pdf_intent_error_text"
5174 rows="3"
5175 cols="50"
5176 placeholder="%s"
5177 %s>%s</textarea>',
5178 esc_attr__('Enter error text', 'mxchat'),
5179 esc_attr($disabled),
5180 isset($this->options['pdf_intent_error_text'])
5181 ? esc_textarea($this->options['pdf_intent_error_text'])
5182 : esc_textarea($default_text)
5183 );
5184 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5185
5186 if (!$this->is_activated) {
5187 echo '<div class="pro-feature-overlay">';
5188 echo '<a href="https://mxchat.ai/" target="_blank">';
5189 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5190 echo '</a>';
5191 echo '</div>';
5192 }
5193 echo '</div>';
5194 }
5195
5196 public function mxchat_pdf_max_pages_callback() {
5197 $disabled = $this->is_activated ? '' : 'disabled';
5198 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5199 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5200
5201 echo '<div class="' . esc_attr($class) . '">';
5202 echo sprintf(
5203 '<input type="range"
5204 id="pdf_max_pages"
5205 name="pdf_max_pages"
5206 min="1"
5207 max="69"
5208 value="%d"
5209 %s
5210 class="range-slider" />',
5211 esc_attr($max_pages),
5212 esc_attr($disabled)
5213 );
5214 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5215 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5216
5217 if (!$this->is_activated) {
5218 echo '<div class="pro-feature-overlay">';
5219 echo '<a href="https://mxchat.ai/" target="_blank">';
5220 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5221 echo '</a>';
5222 echo '</div>';
5223 }
5224 echo '</div>';
5225 }
5226
5227 public function mxchat_live_agent_status_callback() {
5228 $disabled = $this->is_activated ? '' : 'disabled';
5229 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5230 $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off';
5231
5232 echo '<div class="' . esc_attr($class) . '">';
5233 echo '<label class="toggle-switch">';
5234 echo sprintf(
5235 '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s %s />',
5236 checked($status, 'on', false),
5237 esc_attr($disabled)
5238 );
5239 echo '<span class="slider"></span>';
5240 echo '</label>';
5241 echo '<label for="live_agent_status" class="mxchat-status-label">';
5242 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5243 echo '</label>';
5244
5245 if (!$this->is_activated) {
5246 echo '<div class="pro-feature-overlay">';
5247 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>';
5248 echo '</div>';
5249 }
5250 echo '</div>';
5251 }
5252
5253
5254 // Away Message Callback
5255 public function mxchat_live_agent_away_message_callback() {
5256 $disabled = $this->is_activated ? '' : 'disabled';
5257 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5258 $message = isset($this->options['live_agent_away_message'])
5259 ? $this->options['live_agent_away_message']
5260 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5261
5262 echo '<div class="' . esc_attr($class) . '">';
5263 printf(
5264 '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50" %s>%s</textarea>',
5265 esc_attr($disabled),
5266 esc_textarea($message)
5267 );
5268 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5269
5270 if (!$this->is_activated) {
5271 echo '<div class="pro-feature-overlay">';
5272 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>';
5273 echo '</div>';
5274 }
5275 echo '</div>';
5276 }
5277
5278 public function mxchat_live_agent_notification_message_callback() {
5279 $disabled = $this->is_activated ? '' : 'disabled';
5280 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5281 $message = isset($this->options['live_agent_notification_message'])
5282 ? $this->options['live_agent_notification_message']
5283 : __('Live agent has been notified.', 'mxchat');
5284
5285 echo '<div class="' . esc_attr($class) . '">';
5286 printf(
5287 '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50" %s>%s</textarea>',
5288 esc_attr($disabled),
5289 esc_textarea($message)
5290 );
5291 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5292
5293 if (!$this->is_activated) {
5294 echo '<div class="pro-feature-overlay">';
5295 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>';
5296 echo '</div>';
5297 }
5298 echo '</div>';
5299 }
5300
5301 public function mxchat_live_agent_webhook_url_callback() {
5302 $disabled = $this->is_activated ? '' : 'disabled';
5303 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5304 $webhook_url = isset($this->options['live_agent_webhook_url'])
5305 ? esc_url($this->options['live_agent_webhook_url'])
5306 : esc_url(get_option('live_agent_webhook_url', ''));
5307
5308 echo '<div class="' . esc_attr($class) . '">';
5309 printf(
5310 '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" %s />',
5311 $webhook_url,
5312 esc_attr($disabled)
5313 );
5314 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5315 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
5316
5317 if (!$this->is_activated) {
5318 echo '<div class="pro-feature-overlay">';
5319 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>';
5320 echo '</div>';
5321 }
5322 echo '</div>';
5323 }
5324
5325
5326 public function mxchat_similarity_threshold_callback() {
5327 // Load from mxchat_options array
5328 $options = get_option('mxchat_options', []);
5329
5330 // Get value with backwards compatibility
5331 $threshold = isset($options['similarity_threshold'])
5332 ? $options['similarity_threshold']
5333 : get_option('mxchat_similarity_threshold', 80);
5334
5335 echo '<div class="slider-container">';
5336 echo sprintf(
5337 '<input type="range"
5338 id="similarity_threshold"
5339 name="similarity_threshold"
5340 min="70"
5341 max="85"
5342 step="1"
5343 value="%s"
5344 class="range-slider" />',
5345 esc_attr($threshold)
5346 );
5347 echo sprintf(
5348 '<span id="threshold_value" class="range-value">%s</span>',
5349 esc_html($threshold)
5350 );
5351 echo '</div>';
5352
5353 echo '<p class="description">';
5354 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');
5355 echo '</p>';
5356 }
5357
5358
5359
5360 public function mxchat_live_agent_secret_key_callback() {
5361 $disabled = $this->is_activated ? '' : 'disabled';
5362 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5363
5364 echo '<div class="' . esc_attr($class) . '">';
5365 printf(
5366 '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" %s />',
5367 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
5368 esc_attr($disabled)
5369 );
5370 echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5371 echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5372
5373 if (!$this->is_activated) {
5374 echo '<div class="pro-feature-overlay">';
5375 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>';
5376 echo '</div>';
5377 }
5378 echo '</div>';
5379 }
5380
5381 public function mxchat_live_agent_bot_token_callback() {
5382 $disabled = $this->is_activated ? '' : 'disabled';
5383 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5384
5385 echo '<div class="' . esc_attr($class) . '">';
5386 printf(
5387 '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" %s />',
5388 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
5389 esc_attr($disabled)
5390 );
5391 echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5392 echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5393
5394 if (!$this->is_activated) {
5395 echo '<div class="pro-feature-overlay">';
5396 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>';
5397 echo '</div>';
5398 }
5399 echo '</div>';
5400 }
5401
5402 public function mxchat_enqueue_admin_assets() {
5403 wp_enqueue_style('wp-color-picker');
5404
5405 // Get the plugin version or file modification time for cache busting
5406 $plugin_version = '2.0.2'; // Replace this with your plugin's version
5407
5408 // File paths
5409 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5410 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5411 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5412 $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5413 $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5414 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
5415
5416 // Check if files exist and get modification times
5417 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5418 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5419 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5420 $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5421 $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5422 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5423
5424 // Enqueue scripts and styles with corrected paths
5425 wp_enqueue_script(
5426 'mxchat-color-picker',
5427 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
5428 array('wp-color-picker'),
5429 $color_picker_version,
5430 true
5431 );
5432
5433 wp_enqueue_script(
5434 'mxchat-embedding-check',
5435 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
5436 array(),
5437 $embedding_check_version,
5438 true
5439 );
5440
5441 wp_enqueue_script(
5442 'mxchat-transcripts-js',
5443 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5444 array('jquery'),
5445 $transcripts_js_version,
5446 true
5447 );
5448
5449 wp_enqueue_script(
5450 'mxchat-admin-js',
5451 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5452 array('jquery'),
5453 $plugin_version,
5454 true
5455 );
5456
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 ));
5463
5464 // Enqueue the admin CSS
5465 wp_enqueue_style(
5466 'mxchat-admin-css',
5467 plugin_dir_url(__FILE__) . '../css/admin-style.css',
5468 array(),
5469 $admin_css_version
5470 );
5471
5472 wp_enqueue_style(
5473 'mxchat-knowledge-css',
5474 plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5475 array(),
5476 $knowledge_css_version
5477 );
5478
5479
5480 wp_enqueue_style(
5481 'mxchat-intent-css',
5482 plugin_dir_url(__FILE__) . '../css/intent-style.css',
5483 array(),
5484 $intent_css_version
5485 );
5486
5487 // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5488 wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5489 'ajax_url' => admin_url( 'admin-ajax.php' ),
5490 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5491 ) );
5492
5493 // Localize the script for color picker and settings
5494 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5495 'ajax_url' => admin_url('admin-ajax.php'),
5496 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5497 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5498 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5499 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5500 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5501 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5502 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5503 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5504 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5505 'icon_color' => $this->options['icon_color'] ?? '#fff',
5506 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5507 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5508 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5509 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5510 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5511 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5512 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5513 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
5514 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
5515 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
5516 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5517 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5518 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5519 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5520 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5521 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5522 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5523 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5524 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5525 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5526 ));
5527 }
5528
5529
5530 public function mxchat_sanitize($input) {
5531 $new_input = array();
5532
5533 if (isset($input['api_key'])) {
5534 $new_input['api_key'] = sanitize_text_field($input['api_key']);
5535 }
5536
5537 if (isset($input['similarity_threshold'])) {
5538 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5539 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
5540 }
5541
5542 if (isset($input['xai_api_key'])) {
5543 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
5544 }
5545
5546 if (isset($input['claude_api_key'])) {
5547 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5548 }
5549
5550 if (isset($input['deepseek_api_key'])) {
5551 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5552 }
5553
5554 if (isset($input['enable_woocommerce_integration'])) {
5555 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5556 }
5557
5558 if (isset($input['privacy_toggle'])) {
5559 $new_input['privacy_toggle'] = $input['privacy_toggle'];
5560 }
5561
5562 if (isset($input['complianz_toggle'])) {
5563 $new_input['complianz_toggle'] = $input['complianz_toggle'];
5564 }
5565
5566 // Handle custom privacy text input
5567 if (isset($input['privacy_text'])) {
5568 // Allow basic HTML for links
5569 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
5570 }
5571
5572 if (isset($input['system_prompt_instructions'])) {
5573 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
5574 }
5575
5576 if (isset($input['mxchat_pro_email'])) {
5577 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
5578 }
5579
5580 if (isset($input['mxchat_activation_key'])) {
5581 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
5582 }
5583
5584 if (isset($input['append_to_body'])) {
5585 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5586 }
5587
5588 if (isset($input['top_bar_title'])) {
5589 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5590 }
5591
5592 if (isset($input['enable_email_block'])) {
5593 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5594 }
5595
5596 if (isset($input['email_blocker_header_content'])) {
5597 // wp_kses_post() allows standard HTML tags permitted by WordPress
5598 $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
5599 }
5600 if (isset($input['email_blocker_button_text'])) {
5601 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5602 }
5603
5604 if (isset($input['intro_message'])) {
5605 $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
5606 }
5607
5608 if (isset($input['input_copy'])) {
5609 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
5610 }
5611
5612 if (isset($input['rate_limit_message'])) {
5613 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5614 }
5615
5616 if (isset($input['rate_limit_logged_out'])) {
5617 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5618 $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
5619
5620 if (in_array($rate_limit, $allowed_limits, true)) {
5621 $new_input['rate_limit_logged_out'] = $rate_limit;
5622 } else {
5623 $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
5624 }
5625 }
5626
5627 // Handle role rate limits
5628 if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) {
5629 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5630 $new_input['role_rate_limits'] = array();
5631
5632 foreach ($input['role_rate_limits'] as $role_id => $rate_limit) {
5633 $rate_limit = sanitize_text_field($rate_limit);
5634 if (in_array($rate_limit, $allowed_limits, true)) {
5635 $new_input['role_rate_limits'][$role_id] = $rate_limit;
5636 } else {
5637 $new_input['role_rate_limits'][$role_id] = '100'; // Default
5638 }
5639 }
5640 }
5641
5642 if (isset($input['pre_chat_message'])) {
5643 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5644 }
5645
5646 if (isset($input['model'])) {
5647 $allowed_models = array(
5648 'grok-beta',
5649 'grok-2',
5650 'deepseek-chat',
5651 'claude-3-5-sonnet-20241022',
5652 'claude-3-opus-20240229',
5653 'claude-3-sonnet-20240229',
5654 'claude-3-haiku-20240307',
5655 'gpt-4o',
5656 'gpt-4o-mini',
5657 'gpt-4-turbo',
5658 'gpt-4',
5659 'gpt-3.5-turbo',
5660 );
5661 if (in_array($input['model'], $allowed_models)) {
5662 $new_input['model'] = sanitize_text_field($input['model']);
5663 }
5664 }
5665
5666 if (isset($input['close_button_color'])) {
5667 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5668 }
5669
5670 if (isset($input['chatbot_bg_color'])) {
5671 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
5672 }
5673
5674 if (isset($input['woocommerce_consumer_key'])) {
5675 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
5676 }
5677
5678 if (isset($input['woocommerce_consumer_secret'])) {
5679 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
5680 }
5681
5682 if (isset($input['user_message_bg_color'])) {
5683 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
5684 }
5685
5686 if (isset($input['user_message_font_color'])) {
5687 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
5688 }
5689
5690 if (isset($input['bot_message_bg_color'])) {
5691 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
5692 }
5693
5694 if (isset($input['bot_message_font_color'])) {
5695 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
5696 }
5697
5698 if (isset($input['live_agent_message_bg_color'])) {
5699 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
5700 }
5701
5702 if (isset($input['live_agent_message_font_color'])) {
5703 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
5704 }
5705
5706 if (isset($input['mode_indicator_bg_color'])) {
5707 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
5708 }
5709
5710 if (isset($input['mode_indicator_font_color'])) {
5711 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
5712 }
5713
5714 if (isset($input['toolbar_icon_color'])) {
5715 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
5716 }
5717
5718 if (isset($input['top_bar_bg_color'])) {
5719 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5720 }
5721
5722 if (isset($input['send_button_font_color'])) {
5723 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5724 }
5725
5726 if (isset($input['chatbot_background_color'])) {
5727 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
5728 }
5729
5730 if (isset($input['icon_color'])) {
5731 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
5732 }
5733
5734 if (isset($input['custom_icon'])) {
5735 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
5736 }
5737
5738 if (isset($input['title_icon'])) {
5739 $new_input['title_icon'] = esc_url_raw($input['title_icon']);
5740 }
5741
5742 if (isset($input['chat_input_font_color'])) {
5743 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
5744 }
5745
5746 // Sanitize link_target_toggle
5747 if (isset($input['link_target_toggle'])) {
5748 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
5749 }
5750
5751 // Sanitize Loops API Key
5752 if (isset($input['loops_api_key'])) {
5753 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
5754 }
5755
5756 if (isset($input['chat_persistence_toggle'])) {
5757 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
5758 }
5759
5760 if (isset($input['popular_question_1'])) {
5761 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
5762 }
5763
5764 if (isset($input['popular_question_2'])) {
5765 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
5766 }
5767
5768 if (isset($input['popular_question_3'])) {
5769 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
5770 }
5771
5772 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
5773 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
5774 }
5775
5776 // Sanitize Loops Mailing List
5777 if (isset($input['loops_mailing_list'])) {
5778 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5779 }
5780
5781 // Sanitize Triggered Phrase Response
5782 if (isset($input['triggered_phrase_response'])) {
5783 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5784 }
5785
5786 if (isset($input['email_capture_response'])) {
5787 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
5788 }
5789
5790 // Sanitize Brave Search Settings
5791 if (isset($input['brave_api_key'])) {
5792 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
5793 }
5794
5795 if (isset($input['brave_image_count'])) {
5796 $image_count = intval($input['brave_image_count']);
5797 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
5798 }
5799
5800 if (isset($input['brave_safe_search'])) {
5801 $allowed = array('strict', 'off');
5802 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
5803 }
5804
5805 if (isset($input['brave_news_count'])) {
5806 $news_count = intval($input['brave_news_count']);
5807 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
5808 }
5809
5810 if (isset($input['brave_country'])) {
5811 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
5812 }
5813
5814 if (isset($input['brave_language'])) {
5815 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
5816 }
5817
5818 if (isset($input['chat_toolbar_toggle'])) {
5819 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5820 }
5821
5822 if (isset($input['pdf_intent_trigger_text'])) {
5823 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5824 }
5825
5826 if (isset($input['pdf_intent_success_text'])) {
5827 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
5828 }
5829
5830 if (isset($input['pdf_intent_error_text'])) {
5831 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
5832 }
5833
5834 if (isset($input['pdf_max_pages'])) {
5835 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
5836 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
5837 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
5838 }
5839 }
5840
5841 if (isset($input['live_agent_webhook_url'])) {
5842 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
5843 }
5844 if (isset($input['live_agent_secret_key'])) {
5845 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
5846 }
5847
5848 // Live Agent Integration
5849 if (isset($input['live_agent_bot_token'])) {
5850 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
5851 }
5852
5853 if (isset($input['live_agent_status'])) {
5854 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
5855 }
5856 if (isset($input['live_agent_away_message'])) {
5857 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
5858 }
5859 if (isset($input['live_agent_notification_message'])) {
5860 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
5861 }
5862
5863 return $new_input;
5864 }
5865
5866
5867 // Method to append the chatbot to the body
5868 public function mxchat_append_chatbot_to_body() {
5869 $options = get_option('mxchat_options');
5870 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
5871 echo do_shortcode('[mxchat_chatbot floating="yes"]');
5872 }
5873 }
5874
5875
5876
5877 private static function mxchat_extract_main_content($html) {
5878 if (empty($html)) {
5879 //error_log('mxchat_extract_main_content: Empty HTML content received');
5880 return '';
5881 }
5882
5883 try {
5884 $dom = new DOMDocument;
5885 libxml_use_internal_errors(true); // Suppress HTML parsing errors
5886
5887 // Simple load of HTML, with @ to suppress warnings
5888 @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
5889
5890 $xpath = new DOMXPath($dom);
5891
5892 // Simplified selectors focusing on common content areas
5893 $selectors = [
5894 '//article',
5895 '//*[@id="content"]',
5896 '//*[@class="entry-content"]',
5897 '//main'
5898 ];
5899
5900 foreach ($selectors as $selector) {
5901 $nodes = $xpath->query($selector);
5902 if ($nodes && $nodes->length > 0) {
5903 $content = '';
5904 foreach ($nodes as $node) {
5905 $content .= $dom->saveHTML($node);
5906 }
5907 if (!empty($content)) {
5908 return $content;
5909 }
5910 }
5911 }
5912
5913 // Fallback: Return the entire body content if no specific selector matches
5914 $body = $dom->getElementsByTagName('body');
5915 if ($body->length > 0) {
5916 return $dom->saveHTML($body->item(0));
5917 }
5918
5919 // Last resort: return the original HTML
5920 return $html;
5921
5922 } catch (Exception $e) {
5923 //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
5924 return $html; // Return original HTML if parsing fails
5925 } finally {
5926 libxml_clear_errors();
5927 }
5928 }
5929
5930
5931 private function mxchat_fetch_loops_mailing_lists($api_key) {
5932 $url = 'https://app.loops.so/api/v1/lists';
5933 $response = wp_remote_get($url, array(
5934 'headers' => array(
5935 'Authorization' => 'Bearer ' . $api_key,
5936 'Content-Type' => 'application/json'
5937 )
5938 ));
5939
5940 if (is_wp_error($response)) {
5941 return array();
5942 }
5943
5944 $body = wp_remote_retrieve_body($response);
5945 $lists = json_decode($body, true);
5946
5947 return isset($lists) && is_array($lists) ? $lists : array();
5948 }
5949
5950
5951
5952
5953
5954 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
5955 if (empty($vec1) || empty($vec2)) {
5956 return 0.0;
5957 }
5958
5959 $dot_product = 0.0;
5960 $norm_a = 0.0;
5961 $norm_b = 0.0;
5962
5963 for ($i = 0; $i < count($vec1); $i++) {
5964 $dot_product += $vec1[$i] * $vec2[$i];
5965 $norm_a += pow($vec1[$i], 2);
5966 $norm_b += pow($vec2[$i], 2);
5967 }
5968
5969 if ($norm_a == 0.0 || $norm_b == 0.0) {
5970 return 0.0;
5971 } else {
5972 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
5973 }
5974 }
5975
5976
5977
5978
5979 }
5980 ?>
5981