PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.1
MxChat – AI Chatbot & Content Generation for WordPress v2.0.1
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.1, at includes/class-mxchat-admin.php

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