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

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