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

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

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