PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.6.2
MxChat – AI Chatbot & Content Generation for WordPress v1.6.2
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / class-mxchat-admin.php

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

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