PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.6.4
MxChat – AI Chatbot & Content Generation for WordPress v1.6.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 1.6.4, at includes/class-mxchat-admin.php

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