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

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