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

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