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

3,225 lines 131.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Admin {
7 private $options;
8 private $chat_count;
9 private $is_activated;
10
11 public function __construct() {
12 $this->options = get_option('mxchat_options');
13 $this->chat_count = get_option('mxchat_chat_count', 0);
14 $this->is_activated = $this->is_license_active();
15
16 // Initialize default options if they are not set
17 if (!$this->options) {
18 $this->initialize_default_options();
19 }
20
21 // Add admin menu and initialize settings
22 add_action('admin_menu', array($this, 'mxchat_add_plugin_page'));
23 add_action('admin_init', array($this, 'mxchat_page_init'));
24 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
25 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
26 add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
27 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
28 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
29 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
31 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
32 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
33 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
34 add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
36 // Add the AJAX handler for logged-in users
37 add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39
40 // Define admin_post actions for form submission handling
41 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
42 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
43 add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
44
45 }
46
47 // Method to check if the license is active
48 private function is_license_active() {
49 $license_status = get_option('mxchat_license_status', 'inactive');
50 return $license_status === 'active';
51 }
52
53 // Initialize default options
54 private function initialize_default_options() {
55 $default_options = array(
56 'api_key' => '',
57 'xai_api_key' => '',
58 'claude_api_key' => '',
59 '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:
60 - Your name is [Chatbot Name].
61 - 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.
62 - Do not make up links to pages. Only use specific links you see in your knowledgebase.
63 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
64 - 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.',
65 'model' => 'gpt-4o',
66 'rate_limit_logged_in' => '100',
67 'rate_limit_logged_out' => '100',
68 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
69 'top_bar_title' => 'MxChat',
70 'intro_message' => 'Hello! How can I assist you today?',
71 'input_copy' => 'How can I assist?',
72 'append_to_body' => 'off',
73 'close_button_color' => '#fff',
74 'chatbot_bg_color' => '#fff',
75 'user_message_bg_color' => '#fff',
76 'user_message_font_color' => '#212121',
77 'bot_message_bg_color' => '#212121',
78 'bot_message_font_color' => '#fff',
79 'top_bar_bg_color' => '#212121',
80 'send_button_font_color' => '#212121',
81 'chat_input_font_color' => '#212121',
82 'chatbot_background_color' => '#212121',
83 'icon_color' => '#fff',
84 'enable_woocommerce_integration' => '0',
85 'link_target_toggle' => 'off',
86 'pre_chat_message' => 'Hey there! Ask me anything!',
87
88 // New fields for Loops Integration
89 'loops_api_key' => '',
90 'loops_mailing_list' => '',
91 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
92 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
93 'popular_question_1' => '',
94 'popular_question_2' => '',
95 'popular_question_3' => '',
96 'pdf_intent_trigger_text' => "Please provide the URL to the PDF you'd like to discuss.",
97 'pdf_intent_success_text' => "I've processed the PDF. What questions do you have about it?",
98 'pdf_intent_error_text' => "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
99 'pdf_max_pages' => 69,
100
101 );
102
103
104 // Merge existing options with defaults
105 $existing_options = get_option('mxchat_options', array());
106 $merged_options = wp_parse_args($existing_options, $default_options);
107
108 // Update the options if they have changed
109 if ($existing_options !== $merged_options) {
110 update_option('mxchat_options', $merged_options);
111 }
112
113 // Update the $this->options property
114 $this->options = $merged_options;
115 }
116
117
118
119 public function mxchat_add_plugin_page() {
120 // Main menu page
121 add_menu_page(
122 'MxChat Settings',
123 'MxChat',
124 'manage_options',
125 'mxchat-max',
126 array($this, 'mxchat_create_admin_page'),
127 'dashicons-testimonial',
128 6
129 );
130
131 // Submenu page for Knowledge
132 add_submenu_page(
133 'mxchat-max',
134 'Prompts',
135 'Knowledge',
136 'manage_options',
137 'mxchat-prompts',
138 array($this, 'mxchat_create_prompts_page')
139 );
140
141 // Submenu page for Chat Transcripts
142 add_submenu_page(
143 'mxchat-max', // Corrected parent slug to match the main menu
144 'Chat Transcripts',
145 'Transcripts',
146 'manage_options',
147 'mxchat-transcripts',
148 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
149 );
150
151 // Submenu page for Intents
152 add_submenu_page(
153 'mxchat-max', // Parent slug
154 'MxChat Intents', // Page title
155 'Intents', // Menu title
156 'manage_options', // Capability
157 'mxchat-intents', // Menu slug
158 array($this, 'mxchat_intents_page_html') // Callback function
159 );
160
161 // Submenu page for Activation Key
162 add_submenu_page(
163 'mxchat-max',
164 'Pro Upgrade',
165 'Pro Upgrade',
166 'manage_options',
167 'mxchat-activation',
168 array($this, 'mxchat_create_activation_page')
169 );
170
171
172 }
173
174
175 public function mxchat_handle_content_submission() {
176 // Check if the form was submitted and the user has sufficient permissions
177 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
178 return;
179 }
180
181 // Verify the nonce field for security
182 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
183 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
184 wp_die('Nonce verification failed.');
185 }
186
187 // Sanitize the content input
188 $article_content = sanitize_textarea_field($_POST['article_content']);
189
190 // Sanitize the URL input (optional URL)
191 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
192
193 // Generate the embedding vector for the content
194 $embedding_vector = $this->mxchat_generate_embedding($article_content);
195
196 global $wpdb;
197 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
198
199 // Check if the 'source_url' column exists in the table and add it if it doesn't
200 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
201 // Use wpdb::query and a prepared statement to avoid SQL injection
202 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
203 }
204
205 if (is_array($embedding_vector)) {
206 // Serialize the embedding vector before storing it
207 $embedding_vector_serialized = serialize($embedding_vector);
208
209 // Insert the content, embedding vector, and source URL into the database, using a prepared statement
210 $inserted = $wpdb->insert(
211 $table_name,
212 array(
213 'article_content' => $article_content,
214 'embedding_vector' => $embedding_vector_serialized,
215 'source_url' => $article_url, // Insert the URL, empty string if not provided
216 ),
217 array(
218 '%s', // Format for article_content (string)
219 '%s', // Format for embedding_vector (serialized string)
220 '%s', // Format for source_url (string)
221 )
222 );
223
224 if ($inserted === false) {
225 //error_log('Error inserting content: ' . $wpdb->last_error);
226 set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
227 } else {
228 set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
229 }
230
231 } else {
232 //error_log('Embedding generation failed for article content: ' . $article_content);
233 set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
234 }
235
236 // Redirect after setting the transient
237 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
238 exit;
239 }
240
241 public function mxchat_display_admin_notice() {
242 // Success notice
243 if ($message = get_transient('mxchat_admin_notice_success')) {
244 ?>
245 <div class="notice notice-success is-dismissible">
246 <p><?php echo esc_html($message); ?></p>
247 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
248 </div>
249 <?php
250 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
251 }
252
253 // Error notice
254 if ($message = get_transient('mxchat_admin_notice_error')) {
255 ?>
256 <div class="notice notice-error is-dismissible">
257 <p><?php echo esc_html($message); ?></p>
258 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
259 </div>
260 <?php
261 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
262 }
263 }
264
265
266
267
268
269 public function mxchat_create_admin_page() {
270 ?>
271 <div class="wrap mxchat-admin">
272 <?php if (!$this->is_activated): ?>
273 <div class="mxchat-pro-banner">
274 <p>
275 Limited-time offer: Get a lifetime MxChat Pro license for just $49.97 until 01.01.25! After that, it switches to an annual license at $99.97/year. Purchase now and be grandfathered into the lifetime deal!
276 <a href="https://mxchat.ai/" target="_blank">Upgrade to MxChat Pro today!</a>
277 </p>
278 </div>
279 <?php endif; ?>
280 <h2 class="admin-title">Mx<span class="admin-emphasis">Chat</span></h2>
281 <h2 class="mxchat-nav-tab-wrapper">
282 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
283 <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
284 <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
285 <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
286 </h2>
287 <form method="post" action="options.php">
288 <?php settings_fields('mxchat_option_group'); ?>
289 <div id="chatbot" class="mxchat-tab-content active">
290 <?php do_settings_sections('mxchat-chatbot'); ?>
291 </div>
292
293
294
295 <div id="embed" class="mxchat-tab-content">
296 <div class="mxchat-settings-section">
297 <h2>WooCommerce Settings</h2>
298 <table class="form-table">
299 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
300 </table>
301 </div>
302
303 <div class="section-divider"></div> <!-- Divider -->
304
305 <div class="mxchat-settings-section">
306 <h2>Loops Settings</h2>
307 <table class="form-table">
308 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
309 </table>
310 </div>
311
312
313 <div class="section-divider"></div> <!-- Divider -->
314
315 <!-- Brave Search Settings Section -->
316 <div class="mxchat-settings-section">
317 <h2>Brave Search Settings</h2>
318 <table class="form-table">
319 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
320 </table>
321 </div>
322
323 <div class="section-divider"></div> <!-- Divider -->
324
325 <!-- Chat with PDF Intent Settings Section -->
326 <div class="mxchat-settings-section">
327 <h2>Chat with PDF Intent Settings</h2>
328 <table class="form-table">
329 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
330 </table>
331 </div>
332
333 </div>
334
335
336
337
338
339 <div id="theme" class="mxchat-tab-content">
340 <?php do_settings_sections('mxchat-theme'); ?>
341 </div>
342 <div id="general" class="mxchat-tab-content">
343 <?php do_settings_sections('mxchat-general'); ?>
344 <p>
345 If you’re having trouble with setup or getting the responses you need, we encourage you to review our
346 <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer">documentation</a> or
347 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer">create a support ticket</a>.
348 </p>
349 <p>
350 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>.
351 </p>
352
353 <div class="faq-item">
354 <h3>How does the Claude API integration work?</h3>
355 <p>
356 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.
357 </p>
358 <p>
359 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.
360 </p>
361 <p>
362 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>.
363 </p>
364 </div>
365
366 <div class="faq-item">
367 <h3>How does the X.AI API integration work?</h3>
368 <p>
369 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.
370 </p>
371 <p>
372 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>.
373 </p>
374 </div>
375
376 <div class="faq-item">
377 <h3>Do I need an OpenAI API key to use the chatbot?</h3>
378 <p>
379 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.
380 </p>
381 <p>
382 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.
383 </p>
384 </div>
385
386 <div class="faq-item">
387 <h3>How do I add the chatbot to my site?</h3>
388 <p>
389 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.
390 </p>
391 </div>
392
393 <div class="faq-item">
394 <h3>How does the chatbot use my content to generate responses?</h3>
395 <p>
396 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.
397 </p>
398 </div>
399
400 <div class="faq-item">
401 <h3>Why does the chatbot sometimes make up links or information?</h3>
402 <p>
403 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.
404 </p>
405 </div>
406
407 <div class="faq-item">
408 <h3>How do intents work in MxChat?</h3>
409 <p>
410 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>.
411 </p>
412 </div>
413
414
415 <div class="faq-item">
416 <h3>How does the Complianz integration work?</h3>
417 <p>
418 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.
419 </p>
420 </div>
421
422 <div class="faq-item">
423 <h3>How does the WooCommerce integration work?</h3>
424 <p>
425 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.
426 </p>
427 </div>
428
429 <div class="faq-item">
430 <h3>Why isn't my chatbot responding as expected?</h3>
431 <p>
432 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.
433 </p>
434 <p>
435 We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
436 </p>
437 </div>
438
439 <div class="faq-item">
440 <h3>What is Loops, and how do I get an API key?</h3>
441 <p>
442 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.
443 </p>
444 </div>
445
446
447 </div>
448 <?php submit_button(); ?>
449 </form>
450 </div>
451 <?php
452 }
453
454
455 public function mxchat_create_transcripts_page() {
456 ?>
457 <div class="wrap mxchat-admin">
458 <h2><?php esc_html_e('Chat Transcripts', 'mxchat'); ?></h2>
459 <form id="mxchat-delete-form" method="post">
460 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
461 <div class="mxchat-controls">
462 <label for="mxchat-select-all-transcripts" class="mxchat-select-all-label">
463 <input type="checkbox" id="mxchat-select-all-transcripts" /> <?php esc_html_e('Select All', 'mxchat'); ?>
464 </label>
465 <input type="submit" value="<?php esc_attr_e('Delete Selected', 'mxchat'); ?>" class="button delete-chats-button" />
466 </div>
467 <div id="mxchat-transcripts">
468 <!-- Transcripts will be loaded here -->
469 </div>
470 </form>
471 </div>
472 <?php
473 }
474
475
476
477
478 public function mxchat_create_prompts_page() {
479 global $wpdb;
480 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
481
482 // Display success message if all prompts were deleted
483 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
484 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
485 }
486
487 // Set up pagination and search query
488 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
489 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
490 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
491 $per_page = 10;
492 $offset = ($current_page - 1) * $per_page;
493
494 // Modify query to handle search input
495 $sql_search = "";
496 if ($search_query) {
497 $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
498 }
499
500 // Retrieve total number of prompts, considering search filter
501 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
502 $total_pages = ceil($total_prompts / $per_page);
503
504 // Retrieve prompts from the database
505 $prompts = $wpdb->get_results(
506 $wpdb->prepare(
507 "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
508 $per_page,
509 $offset
510 )
511 );
512
513 ?>
514
515 <div class="wrap mxchat-admin">
516 <div class="mxchat-grid-container">
517 <!-- Submit Content Form -->
518 <div class="mxchat-grid-item full-width">
519 <h2>Submit Content</h2>
520 <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
521 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
522 <div class="mxchat-form-group">
523 <label for="article_content">Article Content:</label>
524 <textarea name="article_content" id="article_content" required></textarea>
525 </div>
526 <div class="mxchat-form-group">
527 <label for="article_url">Article URL (Optional):</label>
528 <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
529 </div>
530 <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
531 </form>
532 <div id="mxchat-content-loading" class="mxchat-content-spinner" style="display: none;"></div>
533 <div id="mxchat-content-loading-text" class="mxchat-loading-text">Submitting content, please wait...</div>
534 </div>
535
536 <!-- Submit Sitemap Form -->
537 <div class="mxchat-grid-item">
538 <h2>Submit Sitemap or Page URL</h2>
539 <form id="mxchat-sitemap-form" method="post" class="mxchat-sitemap-form" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
540 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
541 <div class="mxchat-search-group">
542 <input type="url" name="sitemap_url" id="sitemap_url" placeholder="Sitemap or URL" required />
543 <input type="submit" name="submit_sitemap" value="Submit" class="button button-primary" />
544 </div>
545 </form>
546 <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"></div>
547 <div id="mxchat-loading-text" style="display: none;">Loading sitemap content into database, please wait...</div>
548 </div>
549
550 <!-- Search Knowledge Form -->
551 <div class="mxchat-grid-item">
552 <h2>Search Knowledge</h2>
553 <form method="get" id="knowledge-search">
554 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
555 <input type="hidden" name="page" value="mxchat-prompts" />
556 <div class="mxchat-search-group">
557 <input type="text" name="search" placeholder="Search Knowledge" value="<?php echo esc_attr($search_query); ?>" />
558 <input type="submit" value="Search" class="button button-primary" />
559 </div>
560 </form>
561 </div>
562 </div>
563
564 <!-- Table navigation with Delete All Button -->
565 <div class="tablenav">
566 <div class="tablenav-pages">
567 <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
568
569 <!-- Delete All Prompts Button -->
570 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>" style="display: inline;" onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all prompts? This action cannot be undone.', 'mxchat'); ?>');">
571 <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
572 <input type="submit" name="delete_all_prompts" value="<?php esc_attr_e('Delete All Knowledge', 'mxchat'); ?>" class="button mxchat-delete-all" />
573 </form>
574
575 <?php
576 // Display pagination links
577 $page_links = paginate_links(array(
578 'base' => add_query_arg(array('paged' => '%#%', 'search' => urlencode($search_query), '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')), admin_url('admin.php?page=mxchat-prompts')),
579 'format' => '',
580 'prev_text' => __('&laquo; Previous'),
581 'next_text' => __('Next &raquo;'),
582 'total' => $total_pages,
583 'current' => $current_page,
584 ));
585
586 if ($page_links) {
587 echo '<div class="tablenav-pages">' . wp_kses_post($page_links) . '</div>';
588 }
589 ?>
590 </div>
591 </div>
592
593 <!-- Prompts Table -->
594 <table class="mxchat-table">
595 <thead>
596 <tr>
597 <th>ID</th>
598 <th>Article Content</th>
599 <th>URL</th>
600 <th>Actions</th>
601 </tr>
602 </thead>
603 <tbody>
604 <?php if ($prompts) : ?>
605 <?php foreach ($prompts as $prompt) : ?>
606 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
607 <td><?php echo esc_html($prompt->id); ?></td>
608 <td class="mxchat_article_content_dashboard">
609 <span class="content-view"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></span>
610 <textarea class="content-edit" style="display:none;"><?php echo esc_textarea($prompt->article_content); ?></textarea>
611 </td>
612 <td class="mxchat_article_url_dashboard">
613 <span class="url-view">
614 <?php if (!empty($prompt->source_url)) : ?>
615 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank"><?php echo esc_html($prompt->source_url); ?></a>
616 <?php else : ?>
617 N/A
618 <?php endif; ?>
619 </span>
620 <input type="url" class="url-edit" value="<?php echo esc_attr($prompt->source_url); ?>" style="display:none;" />
621 </td>
622 <td>
623 <button class="button edit-button" data-id="<?php echo esc_attr($prompt->id); ?>">Edit</button>
624 <button class="button save-button" data-id="<?php echo esc_attr($prompt->id); ?>" style="display:none;">Save</button>
625 <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>
626 </td>
627 </tr>
628 <?php endforeach; ?>
629 <?php else : ?>
630 <tr>
631 <td colspan="4"><?php esc_html_e('No prompts found.', 'mxchat'); ?></td>
632 </tr>
633 <?php endif; ?>
634 </tbody>
635 </table>
636 </div>
637 <?php
638 }
639
640
641 // Delete All Prompts
642 public function mxchat_handle_delete_all_prompts() {
643 // Verify nonce
644 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
645 wp_die(__('Nonce verification failed.', 'mxchat'));
646 }
647
648 // Check permissions
649 if (!current_user_can('manage_options')) {
650 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
651 }
652
653 global $wpdb;
654 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
655
656 // Delete all prompts from the table
657 $wpdb->query("DELETE FROM {$table_name}");
658
659 // Clear relevant cache
660 wp_cache_delete('all_prompts', 'mxchat_prompts');
661
662 // Redirect back with a success message
663 $redirect_url = add_query_arg(array(
664 'page' => 'mxchat-prompts',
665 'all_deleted' => 'true'
666 ), admin_url('admin.php'));
667
668 wp_safe_redirect($redirect_url);
669 exit;
670 }
671
672 // Single Prompt Deletion
673 public function mxchat_handle_delete_prompt() {
674 // Sanitize and validate nonce
675 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
676 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
677 wp_die('Nonce verification failed.');
678 }
679
680 // Check permissions
681 if (!current_user_can('manage_options')) {
682 wp_die('You do not have sufficient permissions to delete prompts.');
683 }
684
685 // Validate and sanitize ID parameter
686 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
687 if ($id <= 0) {
688 wp_die('Invalid prompt ID.');
689 }
690
691 global $wpdb;
692 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
693
694 // Clear cache and delete prompt
695 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
696 $wpdb->delete($table_name, array('id' => $id), array('%d'));
697
698 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
699 exit;
700 }
701
702
703
704
705 public function mxchat_generate_embedding($text) {
706 $options = get_option('mxchat_options');
707 $api_key = $options['api_key'] ?? 'default_api_key';
708
709 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
710 'body' => wp_json_encode(array(
711 'model' => 'text-embedding-ada-002',
712 'input' => $text
713 )),
714 'headers' => array(
715 'Authorization' => 'Bearer ' . $api_key,
716 'Content-Type' => 'application/json'
717 ),
718 ));
719
720 if (is_wp_error($response)) {
721 return null;
722 }
723
724 $response_data = json_decode(wp_remote_retrieve_body($response), true);
725 return $response_data['data'][0]['embedding'] ?? null;
726 }
727
728 public function mxchat_delete_chat_history() {
729 if (!current_user_can('manage_options')) {
730 echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
731 wp_die();
732 }
733
734 check_ajax_referer('mxchat_delete_chat_history', 'security');
735
736 global $wpdb;
737 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
738
739 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
740 foreach ($_POST['delete_session_ids'] as $session_id) {
741 $session_id_sanitized = sanitize_text_field($session_id);
742
743 // Clear relevant cache before deletion
744 $cache_key = 'chat_session_' . $session_id_sanitized;
745 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
746
747 // Perform the deletion
748 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
749 }
750
751 // Optionally, clear a general cache if you have one
752 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
753
754 echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
755 } else {
756 echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
757 }
758
759 wp_die();
760 }
761
762
763 public function mxchat_save_inline_prompt() {
764 // Check for nonce security
765 check_ajax_referer('mxchat_save_inline_nonce');
766
767 // Verify permissions
768 if (!current_user_can('manage_options')) {
769 wp_send_json_error('Permission denied.');
770 return;
771 }
772
773 global $wpdb;
774 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
775
776 // Validate and sanitize input data
777 $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
778 $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
779 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
780
781 if ($prompt_id > 0 && !empty($article_content)) {
782 // Re-generate the embedding vector for the updated content
783 $embedding_vector = $this->mxchat_generate_embedding($article_content);
784
785 if (is_array($embedding_vector)) {
786 // Serialize the embedding vector before storing it
787 $embedding_vector_serialized = serialize($embedding_vector);
788
789 // Update the prompt in the database
790 $updated = $wpdb->update(
791 $table_name,
792 array(
793 'article_content' => $article_content,
794 'embedding_vector' => $embedding_vector_serialized,
795 'source_url' => $article_url,
796 ),
797 array('id' => $prompt_id),
798 array('%s', '%s', '%s'),
799 array('%d')
800 );
801
802 if ($updated !== false) {
803 wp_send_json_success();
804 } else {
805 wp_send_json_error('Database update failed.');
806 }
807 } else {
808 wp_send_json_error('Embedding generation failed.');
809 }
810 } else {
811 wp_send_json_error('Invalid data.');
812 }
813 }
814
815
816 public function mxchat_fetch_chat_history() {
817 global $wpdb;
818 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
819
820 // Check if the current user has sufficient permissions
821 if (!current_user_can('manage_options')) {
822 echo esc_html__('You do not have sufficient permissions to view this page.', 'mxchat');
823 wp_die();
824 }
825
826 // Fetch chat transcripts from the database, ordered by timestamp
827 $chat_transcripts = $wpdb->get_results(
828 $wpdb->prepare("SELECT * FROM {$table_name} ORDER BY timestamp ASC")
829 );
830
831 // If no transcripts are available, display a message
832 if (empty($chat_transcripts)) {
833 echo esc_html__('No chat history available.', 'mxchat');
834 wp_die();
835 }
836
837 ob_start();
838 $current_session_id = '';
839
840 foreach ($chat_transcripts as $transcript) {
841 // Start a new session block if session ID changes
842 if ($current_session_id !== $transcript->session_id) {
843 if ($current_session_id !== '') {
844 echo '</div>'; // Close the previous session block
845 }
846 $current_session_id = sanitize_text_field($transcript->session_id);
847 echo '<div class="chat-session">';
848 echo '<h4><input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($transcript->session_id) . '"> ' . esc_html__('Session ID:', 'mxchat') . ' ' . esc_html($current_session_id) . '</h4>';
849 }
850
851 // Format the timestamp for display
852 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
853
854 // Determine the role to display (user identifier or email for users, bot for the AI)
855 $role = $transcript->role;
856 if ($role === 'user') {
857 // Sanitize email if available
858 if (!empty($transcript->user_email)) {
859 $role = sanitize_email($transcript->user_email);
860 } else {
861 // Sanitize user identifier, anonymize if it's an IP address
862 $user_identifier = sanitize_text_field($transcript->user_identifier);
863
864 // Check if the user identifier is an IP address and anonymize it
865 if (filter_var($user_identifier, FILTER_VALIDATE_IP)) {
866 $role = preg_replace('/\.\d+$/', '.xxx', $user_identifier); // Mask the last octet
867 } else {
868 $role = $user_identifier; // If it's not an IP, just display the identifier
869 }
870 }
871 }
872
873 // Output the chat message
874 echo '<div class="chat-message">';
875 echo '<strong>' . esc_html($role) . ' (' . esc_html($formatted_timestamp) . '):</strong> ';
876 echo wp_kses_post($transcript->message);
877 echo '</div>';
878 }
879
880 // Close the final session block
881 echo '</div>';
882
883 $output = ob_get_clean();
884
885 echo $output;
886 wp_die();
887 }
888
889
890 public function mxchat_create_activation_page() {
891 $license_status = get_option('mxchat_license_status', 'inactive');
892 $license_error = get_option('mxchat_license_error', '');
893 ?>
894 <div class="wrap mxchat-admin">
895 <h2>MxChat Pro: Activation</h2>
896 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
897 <div class="error notice">
898 <p><?php echo esc_html($license_error); ?></p>
899 </div>
900 <?php endif; ?>
901
902 <form id="mxchat-activation-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
903 <table class="form-table">
904 <tr valign="top">
905 <th scope="row">Email Address</th>
906 <td>
907 <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 />
908 </td>
909 </tr>
910 <tr valign="top">
911 <th scope="row">Activation Key</th>
912 <td>
913 <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 />
914 </td>
915 </tr>
916 </table>
917 <?php if ($license_status !== 'active'): ?>
918 <button type="submit" id="activate_license_button" class="button button-primary"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
919 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
920 <?php endif; ?>
921 </form>
922
923 <!-- License Status Display -->
924 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
925 </div>
926 <?php
927 }
928
929 public function mxchat_intents_page_html() {
930 if ( ! current_user_can( 'manage_options' ) ) {
931 return;
932 }
933
934 // Fetch existing intents with pagination and filtering
935 global $wpdb;
936 $table_name = $wpdb->prefix . 'mxchat_intents';
937 $page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
938 $per_page = 20;
939 $offset = ( $page - 1 ) * $per_page;
940
941 // Build the WHERE clause based on filters
942 $where = '1=1';
943 $search_term = isset( $_GET['s'] ) ? trim( $_GET['s'] ) : '';
944 $callback_filter = isset( $_GET['callback_filter'] ) ? sanitize_text_field( $_GET['callback_filter'] ) : '';
945
946 if ( $search_term ) {
947 $search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
948 $where .= $wpdb->prepare( ' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like );
949 }
950
951 if ( $callback_filter ) {
952 $where .= $wpdb->prepare( ' AND callback_function = %s', $callback_filter );
953 }
954
955 // Get total count for pagination
956 $total_intents = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE $where" );
957 $total_pages = ceil( $total_intents / $per_page );
958
959 // Fetch intents with pagination and filters
960 $intents = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset ) );
961
962 // Get available callback functions with 'pro_only' flag
963 $available_callbacks = $this->mxchat_get_available_callbacks();
964 ?>
965
966 <div class="wrap mxchat-admin">
967 <!-- Add Intent Form -->
968 <form id="mxchat-add-intent-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
969 <input type="hidden" name="action" value="mxchat_add_intent">
970 <?php wp_nonce_field( 'mxchat_add_intent_nonce' ); ?>
971 <h2><?php esc_html_e( 'Add New Intent', 'mxchat' ); ?></h2>
972 <p class="description">
973 <?php esc_html_e( 'We highly encourage users to quickly read our ', 'mxchat' ); ?>
974 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
975 <?php esc_html_e( 'documentation', 'mxchat' ); ?>
976 </a>
977 <?php esc_html_e( ' to better understand intents. Some intents require setup in the Integration tab.', 'mxchat' ); ?>
978 </p>
979
980 <div class="mxchat-form-group">
981 <label for="intent_label"><?php esc_html_e( 'Intent Label (For your reference only)', 'mxchat' ); ?></label>
982 <input name="intent_label" type="text" id="intent_label" class="regular-text" required
983 placeholder="Example Email Capture: Newsletter Signup">
984 </div>
985 <div class="mxchat-form-group">
986 <label for="phrases"><?php esc_html_e( 'Phrases (comma-separated)', 'mxchat' ); ?></label>
987 <textarea name="phrases" id="phrases" rows="5" class="large-text" required
988 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>
989 </div>
990 <div class="mxchat-form-group">
991 <label for="callback_function"><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></label>
992 <select name="callback_function" id="callback_function" required>
993 <option value=""><?php esc_html_e( 'Select a Callback', 'mxchat' ); ?></option>
994 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
995 <?php
996 $label = $callback_data['label'];
997 $pro_only = $callback_data['pro_only'];
998 $disabled = ( ! $this->is_activated && $pro_only ) ? 'disabled' : '';
999 $label_suffix = ( ! $this->is_activated && $pro_only ) ? ' (Pro Only)' : '';
1000 ?>
1001 <option value="<?php echo esc_attr( $function ); ?>" <?php echo $disabled; ?>>
1002 <?php echo esc_html( $label . $label_suffix ); ?>
1003 </option>
1004 <?php endforeach; ?>
1005 </select>
1006 </div>
1007 <button type="submit" class="button button-primary submit-content-button">
1008 <?php esc_html_e( 'Add Intent', 'mxchat' ); ?>
1009 </button>
1010 <div id="mxchat-intent-loading" style="display: none;"></div>
1011 <div id="mxchat-intent-loading-text" style="display: none;">
1012 <?php esc_html_e( 'Saving intent, please wait...', 'mxchat' ); ?>
1013 </div>
1014
1015 </form>
1016
1017 <!-- Filter Form -->
1018 <form method="get" action="">
1019 <input type="hidden" name="page" value="mxchat-intents">
1020 <div class="mxchat-search-group">
1021 <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e( 'Search Intents', 'mxchat' ); ?>" value="<?php echo esc_attr( $search_term ); ?>">
1022 <select name="callback_filter" id="mxchat-callback-filter">
1023 <option value=""><?php esc_html_e( 'All Callbacks', 'mxchat' ); ?></option>
1024 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1025 <?php $label = $callback_data['label']; ?>
1026 <option value="<?php echo esc_attr( $function ); ?>" <?php selected( $callback_filter, $function ); ?>><?php echo esc_html( $label ); ?></option>
1027 <?php endforeach; ?>
1028 </select>
1029 <button type="submit" class="button"><?php esc_html_e( 'Filter', 'mxchat' ); ?></button>
1030 </div>
1031 </form>
1032
1033 <!-- Intents Table -->
1034 <table class="wp-list-table mxchat-intents-table widefat fixed striped">
1035 <thead>
1036 <tr>
1037 <th><?php esc_html_e( 'Intent Label', 'mxchat' ); ?></th>
1038 <th><?php esc_html_e( 'Phrases', 'mxchat' ); ?></th>
1039 <th><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></th>
1040 <th><?php esc_html_e( 'Similarity Threshold', 'mxchat' ); ?></th>
1041 <th><?php esc_html_e( 'Actions', 'mxchat' ); ?></th>
1042 </tr>
1043 </thead>
1044 <tbody>
1045 <?php if ( $intents ) : ?>
1046 <?php foreach ( $intents as $intent ) : ?>
1047 <?php
1048 $callback_function = $intent->callback_function;
1049 $callback_label = isset( $available_callbacks[ $callback_function ]['label'] ) ? $available_callbacks[ $callback_function ]['label'] : $callback_function;
1050 $threshold_value = isset( $intent->similarity_threshold ) ? round( $intent->similarity_threshold * 100 ) : 85;
1051 ?>
1052 <tr>
1053 <td><?php echo esc_html( $intent->intent_label ); ?></td>
1054 <td><?php echo esc_html( $intent->phrases ); ?></td>
1055 <td><?php echo esc_html( $callback_label ); ?></td>
1056 <!-- Similarity Threshold Column -->
1057 <td>
1058 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
1059 <?php wp_nonce_field( 'mxchat_update_intent_threshold_nonce' ); ?>
1060 <input type="hidden" name="action" value="mxchat_update_intent_threshold">
1061 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1062 <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 + '%'">
1063 <output id="threshold_output_<?php echo esc_attr( $intent->id ); ?>"><?php echo esc_html( $threshold_value ); ?>%</output>
1064 </td>
1065 <!-- Actions Column -->
1066 <td>
1067 <button type="submit" class="button button-primary mxchat-save-button">
1068 <?php esc_html_e( 'Save', 'mxchat' ); ?>
1069 </button>
1070 </form>
1071 <!-- Delete Form -->
1072 <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' ); ?>');">
1073 <?php wp_nonce_field( 'mxchat_delete_intent_nonce' ); ?>
1074 <input type="hidden" name="action" value="mxchat_delete_intent">
1075 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1076 <button type="submit" class="button mxchat-delete-all">
1077 <?php esc_html_e( 'Delete', 'mxchat' ); ?>
1078 </button>
1079 </form>
1080 </td>
1081 </tr>
1082 <?php endforeach; ?>
1083 <?php else : ?>
1084 <tr>
1085 <td colspan="5"><?php esc_html_e( 'No intents found.', 'mxchat' ); ?></td>
1086 </tr>
1087 <?php endif; ?>
1088 </tbody>
1089 </table>
1090 </div>
1091 <?php
1092 }
1093
1094
1095 public function mxchat_handle_update_intent_threshold() {
1096 if ( ! current_user_can( 'manage_options' ) ) {
1097 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1098 }
1099
1100 check_admin_referer('mxchat_update_intent_threshold_nonce');
1101
1102 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
1103 global $wpdb;
1104 $table_name = $wpdb->prefix . 'mxchat_intents';
1105 $intent_id = intval($_POST['intent_id']);
1106 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
1107 $similarity_threshold = $threshold_percentage / 100;
1108
1109 $wpdb->update(
1110 $table_name,
1111 ['similarity_threshold' => $similarity_threshold],
1112 ['id' => $intent_id],
1113 ['%f'],
1114 ['%d']
1115 );
1116 }
1117
1118 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1119 exit;
1120 }
1121
1122
1123 public function mxchat_handle_add_intent() {
1124 if ( ! current_user_can( 'manage_options' ) ) {
1125 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1126 }
1127
1128 check_admin_referer('mxchat_add_intent_nonce');
1129
1130 global $wpdb;
1131 $table_name = $wpdb->prefix . 'mxchat_intents';
1132
1133 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
1134 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
1135 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
1136 $default_threshold = 0.85;
1137
1138 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
1139 wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
1140 }
1141
1142 $available_callbacks = $this->mxchat_get_available_callbacks();
1143
1144 if (!array_key_exists($callback_function, $available_callbacks)) {
1145 wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
1146 }
1147
1148 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
1149 if ($is_pro_only && !$this->is_activated) {
1150 wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
1151 }
1152
1153 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
1154
1155 if (empty($phrases_array)) {
1156 wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
1157 }
1158
1159 $vectors = [];
1160 foreach ($phrases_array as $phrase) {
1161 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1162 if (is_array($embedding_vector)) {
1163 $vectors[] = $embedding_vector;
1164 } else {
1165 wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
1166 }
1167 }
1168
1169 if (!empty($vectors)) {
1170 $combined_vector = $this->mxchat_average_vectors($vectors);
1171 $serialized_vector = maybe_serialize($combined_vector);
1172
1173 $result = $wpdb->insert($table_name, [
1174 'intent_label' => $intent_label,
1175 'phrases' => implode(', ', $phrases_array),
1176 'embedding_vector' => $serialized_vector,
1177 'callback_function' => $callback_function,
1178 'similarity_threshold' => $default_threshold,
1179 ]);
1180
1181 if ($result === false) {
1182 wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
1183 }
1184 } else {
1185 wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
1186 }
1187
1188 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1189 exit;
1190 }
1191
1192
1193
1194
1195
1196
1197 private function mxchat_get_available_callbacks() {
1198 return [
1199 'mxchat_handle_email_capture' => [
1200 'label' => 'Email Capture',
1201 'pro_only' => false,
1202 ],
1203 'mxchat_handle_product_inquiry' => [
1204 'label' => 'Show Product Card',
1205 'pro_only' => true,
1206 ],
1207 'mxchat_handle_order_history' => [
1208 'label' => 'Order History',
1209 'pro_only' => true,
1210 ],
1211 'mxchat_generate_image' => [
1212 'label' => 'Generate Image',
1213 'pro_only' => true,
1214 ],
1215 'mxchat_handle_search_request' => [
1216 'label' => 'Brave Web Search',
1217 'pro_only' => false,
1218 ],
1219 'mxchat_handle_image_search_request' => [
1220 'label' => 'Brave Image Search',
1221 'pro_only' => false,
1222 ],
1223 'mxchat_handle_add_to_cart_intent' => [
1224 'label' => 'Add to Cart',
1225 'pro_only' => true,
1226 ],
1227 'mxchat_handle_checkout_intent' => [
1228 'label' => 'Proceed to Checkout',
1229 'pro_only' => true,
1230 ],
1231 'mxchat_handle_pdf_discussion' => [
1232 'label' => 'Chat with PDF',
1233 'pro_only' => true,
1234 ],
1235 ];
1236 }
1237
1238
1239 private function mxchat_average_vectors($vectors) {
1240 $vector_length = count($vectors[0]);
1241 $sum_vector = array_fill(0, $vector_length, 0);
1242
1243 foreach ($vectors as $vector) {
1244 for ($i = 0; $i < $vector_length; $i++) {
1245 $sum_vector[$i] += $vector[$i];
1246 }
1247 }
1248
1249 // Divide each component by the number of vectors to get the average
1250 $num_vectors = count($vectors);
1251 for ($i = 0; $i < $vector_length; $i++) {
1252 $sum_vector[$i] /= $num_vectors;
1253 }
1254
1255 return $sum_vector;
1256 }
1257
1258 public function mxchat_handle_delete_intent() {
1259 if ( ! current_user_can( 'manage_options' ) ) {
1260 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1261 }
1262
1263 check_admin_referer('mxchat_delete_intent_nonce');
1264
1265 if (isset($_POST['intent_id'])) {
1266 global $wpdb;
1267 $table_name = $wpdb->prefix . 'mxchat_intents';
1268 $intent_id = intval($_POST['intent_id']);
1269
1270 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
1271 }
1272
1273 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1274 exit;
1275 }
1276
1277
1278 public function mxchat_page_init() {
1279 // Register settings
1280 register_setting(
1281 'mxchat_option_group',
1282 'mxchat_options',
1283 array($this, 'mxchat_sanitize')
1284 );
1285
1286 // Chatbot Settings Section
1287 add_settings_section(
1288 'mxchat_chatbot_section',
1289 'Chatbot Settings',
1290 null,
1291 'mxchat-chatbot'
1292 );
1293
1294 // Registering fields for the Chatbot Settings section
1295 add_settings_field(
1296 'api_key',
1297 'OpenAI API Key',
1298 array($this, 'api_key_callback'),
1299 'mxchat-chatbot',
1300 'mxchat_chatbot_section'
1301 );
1302
1303 add_settings_field(
1304 'xai_api_key',
1305 'X.AI API Key',
1306 array($this, 'xai_api_key_callback'),
1307 'mxchat-chatbot',
1308 'mxchat_chatbot_section'
1309 );
1310
1311 add_settings_field(
1312 'claude_api_key',
1313 'Claude API Key',
1314 array($this, 'claude_api_key_callback'),
1315 'mxchat-chatbot',
1316 'mxchat_chatbot_section'
1317 );
1318
1319
1320 add_settings_field(
1321 'system_prompt_instructions',
1322 'AI Instructions',
1323 array($this, 'system_prompt_instructions_callback'),
1324 'mxchat-chatbot',
1325 'mxchat_chatbot_section'
1326 );
1327
1328 add_settings_field(
1329 'model',
1330 'Model',
1331 array($this, 'mxchat_model_callback'),
1332 'mxchat-chatbot',
1333 'mxchat_chatbot_section'
1334 );
1335
1336 add_settings_field(
1337 'top_bar_title',
1338 'Top Bar Title',
1339 array($this, 'mxchat_top_bar_title_callback'),
1340 'mxchat-chatbot',
1341 'mxchat_chatbot_section'
1342 );
1343
1344 add_settings_field(
1345 'intro_message',
1346 'Introductory Message',
1347 array($this, 'mxchat_intro_message_callback'),
1348 'mxchat-chatbot',
1349 'mxchat_chatbot_section'
1350 );
1351
1352 add_settings_field(
1353 'input_copy',
1354 'Input Copy',
1355 array($this, 'mxchat_input_copy_callback'),
1356 'mxchat-chatbot',
1357 'mxchat_chatbot_section'
1358 );
1359
1360 add_settings_field(
1361 'rate_limit_logged_in',
1362 __('Rate Limit for Logged-in Users', 'mxchat'),
1363 array($this, 'mxchat_rate_limit_logged_in_callback'),
1364 'mxchat-chatbot',
1365 'mxchat_chatbot_section'
1366 );
1367
1368 add_settings_field(
1369 'rate_limit_logged_out',
1370 __('Rate Limit for Logged-out Users', 'mxchat'),
1371 array($this, 'mxchat_rate_limit_logged_out_callback'),
1372 'mxchat-chatbot',
1373 'mxchat_chatbot_section'
1374 );
1375
1376 add_settings_field(
1377 'rate_limit_message',
1378 'Rate Limit Message',
1379 array($this, 'mxchat_rate_limit_message_callback'),
1380 'mxchat-chatbot',
1381 'mxchat_chatbot_section'
1382 );
1383
1384 add_settings_field(
1385 'pre_chat_message',
1386 'Pre-Chat Message',
1387 array($this, 'mxchat_pre_chat_message_callback'),
1388 'mxchat-chatbot',
1389 'mxchat_chatbot_section'
1390 );
1391
1392 add_settings_field(
1393 'append_to_body',
1394 'Append Chat Widget to Body',
1395 array($this, 'mxchat_append_to_body_callback'),
1396 'mxchat-chatbot',
1397 'mxchat_chatbot_section'
1398 );
1399
1400 add_settings_field(
1401 'privacy_toggle',
1402 'Toggle Privacy Notice',
1403 array($this, 'mxchat_privacy_toggle_callback'),
1404 'mxchat-chatbot',
1405 'mxchat_chatbot_section'
1406 );
1407
1408 add_settings_field(
1409 'complianz_toggle',
1410 'Enable Complianz',
1411 array($this, 'mxchat_complianz_toggle_callback'),
1412 'mxchat-chatbot',
1413 'mxchat_chatbot_section'
1414 );
1415
1416 add_settings_field(
1417 'link_target_toggle',
1418 'Open Links in a New Tab',
1419 array($this, 'mxchat_link_target_toggle_callback'),
1420 'mxchat-chatbot',
1421 'mxchat_chatbot_section'
1422 );
1423
1424 add_settings_field(
1425 'chat_persistence_toggle',
1426 'Enable Chat Persistence',
1427 array($this, 'mxchat_chat_persistence_toggle_callback'),
1428 'mxchat-chatbot',
1429 'mxchat_chatbot_section'
1430 );
1431
1432 add_settings_field(
1433 'popular_question_1',
1434 'Popular Question 1',
1435 array($this, 'mxchat_popular_question_1_callback'),
1436 'mxchat-chatbot',
1437 'mxchat_chatbot_section'
1438 );
1439
1440 add_settings_field(
1441 'popular_question_2',
1442 'Popular Question 2',
1443 array($this, 'mxchat_popular_question_2_callback'),
1444 'mxchat-chatbot',
1445 'mxchat_chatbot_section'
1446 );
1447
1448 add_settings_field(
1449 'popular_question_3',
1450 'Popular Question 3',
1451 array($this, 'mxchat_popular_question_3_callback'),
1452 'mxchat-chatbot',
1453 'mxchat_chatbot_section'
1454 );
1455
1456
1457
1458 // WooCommerce Settings Section
1459 add_settings_section(
1460 'mxchat_woocommerce_section',
1461 'WooCommerce Settings',
1462 null,
1463 'mxchat-embed'
1464 );
1465
1466 // Loops Settings Section
1467 add_settings_section(
1468 'mxchat_loops_section',
1469 'Loops Settings',
1470 null,
1471 'mxchat-embed'
1472 );
1473
1474 // WooCommerce Settings Fields
1475 add_settings_field(
1476 'enable_woocommerce_integration',
1477 'Automatically Embed Products',
1478 array($this, 'mxchat_enable_woocommerce_integration_callback'),
1479 'mxchat-embed',
1480 'mxchat_woocommerce_section'
1481 );
1482
1483
1484 add_settings_field(
1485 'woocommerce_consumer_key',
1486 'WooCommerce Consumer Key',
1487 array($this, 'mxchat_woocommerce_consumer_key_callback'),
1488 'mxchat-embed',
1489 'mxchat_woocommerce_section'
1490 );
1491
1492 add_settings_field(
1493 'woocommerce_consumer_secret',
1494 'WooCommerce Consumer Secret',
1495 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
1496 'mxchat-embed',
1497 'mxchat_woocommerce_section'
1498 );
1499
1500 // Loops Settings Fields
1501 add_settings_field(
1502 'loops_api_key',
1503 'Loops API Key',
1504 array($this, 'mxchat_loops_api_key_callback'),
1505 'mxchat-embed',
1506 'mxchat_loops_section'
1507 );
1508
1509 add_settings_field(
1510 'loops_mailing_list',
1511 'Loops Mailing List',
1512 array($this, 'mxchat_loops_mailing_list_callback'),
1513 'mxchat-embed',
1514 'mxchat_loops_section'
1515 );
1516
1517 add_settings_field(
1518 'triggered_phrase_response',
1519 'Triggered Phrase Response',
1520 array($this, 'mxchat_triggered_phrase_response_callback'),
1521 'mxchat-embed',
1522 'mxchat_loops_section'
1523 );
1524
1525 add_settings_field(
1526 'email_capture_response',
1527 'Email Capture Response',
1528 array($this, 'mxchat_email_capture_response_callback'),
1529 'mxchat-embed',
1530 'mxchat_loops_section'
1531 );
1532
1533
1534 // Brave Search Settings Fields
1535 add_settings_section(
1536 'mxchat_brave_section',
1537 __('Brave Search Settings', 'mxchat'),
1538 array($this, 'mxchat_brave_section_callback'),
1539 'mxchat-embed'
1540 );
1541
1542 add_settings_field(
1543 'brave_api_key',
1544 __('Brave API Key', 'mxchat'),
1545 array($this, 'mxchat_brave_api_key_callback'),
1546 'mxchat-embed',
1547 'mxchat_brave_section'
1548 );
1549
1550 add_settings_field(
1551 'brave_image_count',
1552 __('Number of Images to Return', 'mxchat'),
1553 array($this, 'mxchat_brave_image_count_callback'),
1554 'mxchat-embed',
1555 'mxchat_brave_section'
1556 );
1557
1558 add_settings_field(
1559 'brave_safe_search',
1560 __('Safe Search', 'mxchat'),
1561 array($this, 'mxchat_brave_safe_search_callback'),
1562 'mxchat-embed',
1563 'mxchat_brave_section'
1564 );
1565
1566 add_settings_field(
1567 'brave_news_count',
1568 __('Number of News Articles', 'mxchat'),
1569 array($this, 'mxchat_brave_news_count_callback'),
1570 'mxchat-embed',
1571 'mxchat_brave_section'
1572 );
1573
1574 add_settings_field(
1575 'brave_country',
1576 __('Country', 'mxchat'),
1577 array($this, 'mxchat_brave_country_callback'),
1578 'mxchat-embed',
1579 'mxchat_brave_section'
1580 );
1581
1582 add_settings_field(
1583 'brave_language',
1584 __('Language', 'mxchat'),
1585 array($this, 'mxchat_brave_language_callback'),
1586 'mxchat-embed',
1587 'mxchat_brave_section'
1588 );
1589
1590
1591 // Chat with PDF Intent Settings Fields
1592 add_settings_section(
1593 'mxchat_pdf_intent_section',
1594 __('Chat with PDF Intent Settings', 'mxchat'),
1595 array($this, 'mxchat_pdf_intent_section_callback'),
1596 'mxchat-embed'
1597 );
1598
1599 add_settings_field(
1600 'pdf_intent_trigger_text',
1601 __('Intent Trigger Text', 'mxchat'),
1602 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
1603 'mxchat-embed',
1604 'mxchat_pdf_intent_section'
1605 );
1606
1607 add_settings_field(
1608 'pdf_intent_success_text',
1609 __('Success Text', 'mxchat'),
1610 array($this, 'mxchat_pdf_intent_success_text_callback'),
1611 'mxchat-embed',
1612 'mxchat_pdf_intent_section'
1613 );
1614
1615 add_settings_field(
1616 'pdf_intent_error_text',
1617 __('Error Text', 'mxchat'),
1618 array($this, 'mxchat_pdf_intent_error_text_callback'),
1619 'mxchat-embed',
1620 'mxchat_pdf_intent_section'
1621 );
1622
1623 // Add PDF Maximum Pages Field
1624 add_settings_field(
1625 'pdf_max_pages',
1626 __('Maximum PDF Pages', 'mxchat'),
1627 array($this, 'mxchat_pdf_max_pages_callback'),
1628 'mxchat-embed',
1629 'mxchat_pdf_intent_section'
1630 );
1631
1632
1633
1634
1635
1636
1637 // Theme Settings Section
1638 add_settings_section(
1639 'mxchat_theme_section',
1640 'Theme Settings',
1641 null,
1642 'mxchat-theme'
1643 );
1644
1645 add_settings_field(
1646 'close_button_color',
1647 'Close Button & Title Color',
1648 array($this, 'mxchat_close_button_color_callback'),
1649 'mxchat-theme',
1650 'mxchat_theme_section'
1651 );
1652
1653 add_settings_field(
1654 'chatbot_bg_color',
1655 'Chatbot Background Color',
1656 array($this, 'mxchat_chatbot_bg_color_callback'),
1657 'mxchat-theme',
1658 'mxchat_theme_section'
1659 );
1660
1661 add_settings_field(
1662 'user_message_bg_color',
1663 'User Message Background Color',
1664 array($this, 'mxchat_user_message_bg_color_callback'),
1665 'mxchat-theme',
1666 'mxchat_theme_section'
1667 );
1668
1669 add_settings_field(
1670 'user_message_font_color',
1671 'User Message Font Color',
1672 array($this, 'mxchat_user_message_font_color_callback'),
1673 'mxchat-theme',
1674 'mxchat_theme_section'
1675 );
1676
1677 add_settings_field(
1678 'bot_message_bg_color',
1679 'Bot Message Background Color',
1680 array($this, 'mxchat_bot_message_bg_color_callback'),
1681 'mxchat-theme',
1682 'mxchat_theme_section'
1683 );
1684
1685 add_settings_field(
1686 'bot_message_font_color',
1687 'Bot Message Font Color',
1688 array($this, 'mxchat_bot_message_font_color_callback'),
1689 'mxchat-theme',
1690 'mxchat_theme_section'
1691 );
1692
1693 add_settings_field(
1694 'top_bar_bg_color',
1695 'Top Bar Background Color',
1696 array($this, 'mxchat_top_bar_bg_color_callback'),
1697 'mxchat-theme',
1698 'mxchat_theme_section'
1699 );
1700
1701 add_settings_field(
1702 'send_button_font_color',
1703 'Send Button Color',
1704 array($this, 'mxchat_send_button_font_color_callback'),
1705 'mxchat-theme',
1706 'mxchat_theme_section'
1707 );
1708
1709 add_settings_field(
1710 'chat_input_font_color',
1711 'Chat Input Font Color',
1712 array($this, 'mxchat_chat_input_font_color_callback'),
1713 'mxchat-theme',
1714 'mxchat_theme_section'
1715 );
1716
1717 add_settings_field(
1718 'chatbot_background_color',
1719 'Floating Widget Background Color',
1720 array($this, 'mxchat_chatbot_background_color_callback'),
1721 'mxchat-theme',
1722 'mxchat_theme_section'
1723 );
1724
1725 add_settings_field(
1726 'icon_color',
1727 'Chatbot Icon Color',
1728 array($this, 'mxchat_icon_color_callback'),
1729 'mxchat-theme',
1730 'mxchat_theme_section'
1731 );
1732
1733 // General Settings Section
1734 add_settings_section(
1735 'mxchat_general_section',
1736 'Frequently Asked Questions (FAQ)',
1737 null,
1738 'mxchat-general'
1739 );
1740
1741
1742 }
1743
1744 public function mxchat_handle_activate_license() {
1745 check_ajax_referer('mxchat_activate_license_nonce', 'security');
1746
1747 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
1748 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
1749
1750 if (empty($license_key) || empty($customer_email)) {
1751 wp_send_json_error('Email or License Key is missing');
1752 }
1753
1754 $product_id = 'MxChatPRO';
1755
1756 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
1757
1758 if (is_wp_error($response)) {
1759 wp_send_json_error('Activation failed due to a server error');
1760 }
1761
1762 $body = wp_remote_retrieve_body($response);
1763 $data = json_decode($body);
1764
1765 if ($data && isset($data->activated) && $data->activated) {
1766 update_option('mxchat_license_status', 'active');
1767 update_option('mxchat_pro_email', $customer_email);
1768 update_option('mxchat_activation_key', $license_key);
1769 wp_send_json_success();
1770 } else {
1771 $error_message = isset($data->error) ? $data->error : 'Activation failed';
1772 update_option('mxchat_license_status', 'inactive');
1773 update_option('mxchat_license_error', $error_message);
1774 wp_send_json_error($error_message);
1775 }
1776 }
1777
1778 public function mxchat_rate_limit_logged_in_callback() {
1779 $rate_limits = array('1', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
1780 $selected_rate_limit = isset($this->options['rate_limit_logged_in']) ? $this->options['rate_limit_logged_in'] : '100';
1781
1782 echo '<div class="pro-feature-wrapper active">';
1783 echo '<select id="rate_limit_logged_in" name="mxchat_options[rate_limit_logged_in]">';
1784 foreach ($rate_limits as $limit) {
1785 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
1786 }
1787 echo '</select>';
1788 echo '<p class="description">Set the rate limit for logged-in users.</p>';
1789 echo '</div>';
1790 }
1791
1792 public function mxchat_rate_limit_logged_out_callback() {
1793 $rate_limits = array('1', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
1794 $selected_rate_limit = isset($this->options['rate_limit_logged_out']) ? $this->options['rate_limit_logged_out'] : '10';
1795
1796 echo '<div class="pro-feature-wrapper active">';
1797 echo '<select id="rate_limit_logged_out" name="mxchat_options[rate_limit_logged_out]">';
1798 foreach ($rate_limits as $limit) {
1799 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
1800 }
1801 echo '</select>';
1802 echo '<p class="description">Set the rate limit for logged-out users.</p>';
1803 echo '</div>';
1804 }
1805
1806
1807 public function mxchat_rate_limit_message_callback() {
1808 // Remove the is_activated check and make it always enabled
1809 echo '<div class="pro-feature-wrapper active">';
1810 printf(
1811 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
1812 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
1813 );
1814 echo '</div>';
1815 }
1816
1817
1818 public function mxchat_enable_woocommerce_integration_callback() {
1819 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
1820 $disabled = $this->is_activated ? '' : 'disabled';
1821 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1822
1823 echo '<div class="' . esc_attr($class) . '">';
1824 echo '<label class="toggle-switch">';
1825 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
1826 echo '<span class="slider"></span>';
1827 echo '</label>';
1828
1829 if (!$this->is_activated) {
1830 echo '<div class="pro-feature-overlay">';
1831 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1832 echo '</div>';
1833 }
1834
1835 echo '</div>';
1836 }
1837
1838
1839
1840
1841 private function mxchat_add_option_field($id, $title, $callback = '') {
1842 add_settings_field(
1843 $id,
1844 $title,
1845 $callback ? array($this, $callback) : array($this, $id . '_callback'),
1846 'mxchat-max',
1847 'mxchat_setting_section_id',
1848 $id === 'model' ? ['label_for' => 'model'] : []
1849 );
1850 }
1851
1852
1853 public function api_key_callback() {
1854 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
1855 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
1856 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
1857 }
1858
1859 public function xai_api_key_callback() {
1860 // Check if the feature is activated (paid feature)
1861 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
1862 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
1863
1864 // Render the input field for the X.AI API key
1865 echo '<div class="' . esc_attr($class) . '">';
1866 printf(
1867 '<input type="password" id="xai_api_key" name="mxchat_options[xai_api_key]" value="%s" class="regular-text" %s />',
1868 isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
1869 $disabled
1870 );
1871 echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
1872
1873 // If the feature is not activated, show the overlay with a "Pro Only" message
1874 if (!$this->is_activated) {
1875 echo '<div class="pro-feature-overlay">';
1876 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1877 echo '</div>';
1878 }
1879
1880 echo '</div>';
1881 }
1882
1883 public function claude_api_key_callback() {
1884 // Check if the feature is activated (paid feature)
1885 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
1886 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
1887
1888 // Render the input field for the Claude API key
1889 echo '<div class="' . esc_attr($class) . '">';
1890 printf(
1891 '<input type="password" id="claude_api_key" name="mxchat_options[claude_api_key]" value="%s" class="regular-text" %s />',
1892 isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
1893 $disabled
1894 );
1895 echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
1896
1897 // If the feature is not activated, show the overlay with a "Pro Only" message
1898 if (!$this->is_activated) {
1899 echo '<div class="pro-feature-overlay">';
1900 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1901 echo '</div>';
1902 }
1903
1904 echo '</div>';
1905 }
1906
1907 public function mxchat_woocommerce_consumer_key_callback() {
1908 $disabled = $this->is_activated ? '' : 'disabled';
1909 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1910
1911 echo '<div class="' . esc_attr($class) . '">';
1912 printf(
1913 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
1914 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
1915 $disabled
1916 );
1917
1918 if (!$this->is_activated) {
1919 echo '<div class="pro-feature-overlay">';
1920 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1921 echo '</div>';
1922 }
1923
1924 echo '</div>';
1925 }
1926
1927 public function mxchat_woocommerce_consumer_secret_callback() {
1928 $disabled = $this->is_activated ? '' : 'disabled';
1929 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1930
1931 echo '<div class="' . esc_attr($class) . '">';
1932 printf(
1933 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
1934 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
1935 $disabled
1936 );
1937 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
1938
1939 if (!$this->is_activated) {
1940 echo '<div class="pro-feature-overlay">';
1941 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1942 echo '</div>';
1943 }
1944
1945 echo '</div>';
1946 }
1947
1948 public function mxchat_loops_api_key_callback() {
1949 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
1950
1951 echo '<div class="api-key-wrapper">';
1952 printf(
1953 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
1954 $loops_api_key
1955 );
1956 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
1957 echo '</div>';
1958 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
1959 }
1960
1961 public function mxchat_loops_mailing_list_callback() {
1962 // Retrieve Loops API key and lists
1963 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
1964 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
1965
1966 if ($loops_api_key) {
1967 // Fetch lists from Loops API
1968 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
1969
1970 if ($lists) {
1971 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
1972 foreach ($lists as $list) {
1973 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
1974 }
1975 echo '</select>';
1976 } else {
1977 echo '<p class="description">No lists found. Please verify your API Key.</p>';
1978 }
1979 } else {
1980 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
1981 }
1982 }
1983
1984 public function mxchat_triggered_phrase_response_callback() {
1985 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
1986 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
1987 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
1988 }
1989
1990 public function mxchat_email_capture_response_callback() {
1991 $email_capture_response = isset($this->options['email_capture_response']) ? $this->options['email_capture_response'] : 'Thank you for providing your email! You\'ve been added to our list.';
1992 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
1993 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
1994 }
1995
1996
1997 public function mxchat_pre_chat_message_callback() {
1998 printf(
1999 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
2000 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
2001 );
2002 }
2003
2004 // Callback for AI Instructions textarea
2005 public function system_prompt_instructions_callback() {
2006 printf(
2007 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
2008 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
2009 );
2010 }
2011
2012 public function mxchat_model_callback() {
2013 $models = array(
2014 'X.AI Models' => array(
2015 'grok-beta' => 'grok-beta (Early Beta)'
2016 ),
2017 'Claude Models' => array(
2018 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
2019 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
2020 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
2021 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
2022 ),
2023 'OpenAI Models' => array(
2024 'gpt-4o' => 'GPT-4o (Recommended)',
2025 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
2026 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
2027 'gpt-4' => 'GPT-4 (High Intelligence)',
2028 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
2029 )
2030 );
2031
2032 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
2033
2034 echo '<select id="model" name="mxchat_options[model]">';
2035
2036 foreach ($models as $group_label => $group_models) {
2037 echo '<optgroup label="' . esc_attr($group_label) . '">';
2038
2039 foreach ($group_models as $model_value => $model_label) {
2040 // Disable if not activated for paid models (Claude or X.AI)
2041 $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
2042 $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
2043
2044 // Output the <option> element
2045 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
2046 }
2047
2048 echo '</optgroup>';
2049 }
2050
2051 echo '</select>';
2052 }
2053
2054
2055
2056 public function mxchat_top_bar_title_callback() {
2057 printf(
2058 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
2059 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
2060 );
2061 }
2062
2063 public function mxchat_intro_message_callback() {
2064 printf(
2065 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
2066 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
2067 );
2068 }
2069
2070 public function mxchat_input_copy_callback() {
2071 printf(
2072 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
2073 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
2074 );
2075 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
2076 }
2077
2078
2079
2080
2081
2082
2083 public function mxchat_close_button_color_callback() {
2084 $disabled = $this->is_activated ? '' : 'disabled';
2085
2086 echo '<div class="pro-feature-wrapper">';
2087 printf(
2088 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
2089 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
2090 esc_attr($disabled)
2091 );
2092
2093 if (!$this->is_activated) {
2094 echo '<div class="pro-feature-overlay">';
2095 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2096 echo '</div>';
2097 }
2098
2099 echo '</div>';
2100 }
2101
2102 public function mxchat_chatbot_bg_color_callback() {
2103 $disabled = $this->is_activated ? '' : 'disabled';
2104
2105 echo '<div class="pro-feature-wrapper">';
2106 printf(
2107 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
2108 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
2109 esc_attr($disabled)
2110 );
2111
2112 if (!$this->is_activated) {
2113 echo '<div class="pro-feature-overlay">';
2114 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2115 echo '</div>';
2116 }
2117
2118 echo '</div>';
2119 }
2120
2121 public function mxchat_user_message_bg_color_callback() {
2122 $disabled = $this->is_activated ? '' : 'disabled';
2123
2124 echo '<div class="pro-feature-wrapper">';
2125 printf(
2126 '<input type="text" id="user_message_bg_color" name="mxchat_options[user_message_bg_color]" value="%s" class="my-color-field" data-default-color="#0078d7" %s />',
2127 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
2128 esc_attr($disabled)
2129 );
2130
2131 if (!$this->is_activated) {
2132 echo '<div class="pro-feature-overlay">';
2133 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2134 echo '</div>';
2135 }
2136
2137 echo '</div>';
2138 }
2139
2140 public function mxchat_user_message_font_color_callback() {
2141 $disabled = $this->is_activated ? '' : 'disabled';
2142
2143 echo '<div class="pro-feature-wrapper">';
2144 printf(
2145 '<input type="text" id="user_message_font_color" name="mxchat_options[user_message_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2146 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
2147 esc_attr($disabled)
2148 );
2149
2150 if (!$this->is_activated) {
2151 echo '<div class="pro-feature-overlay">';
2152 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2153 echo '</div>';
2154 }
2155
2156 echo '</div>';
2157 }
2158
2159 public function mxchat_bot_message_bg_color_callback() {
2160 $disabled = $this->is_activated ? '' : 'disabled';
2161
2162 echo '<div class="pro-feature-wrapper">';
2163 printf(
2164 '<input type="text" id="bot_message_bg_color" name="mxchat_options[bot_message_bg_color]" value="%s" class="my-color-field" data-default-color="#e1e1e1" %s />',
2165 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
2166 esc_attr($disabled)
2167 );
2168
2169 if (!$this->is_activated) {
2170 echo '<div class="pro-feature-overlay">';
2171 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2172 echo '</div>';
2173 }
2174
2175 echo '</div>';
2176 }
2177
2178 public function mxchat_bot_message_font_color_callback() {
2179 $disabled = $this->is_activated ? '' : 'disabled';
2180
2181 echo '<div class="pro-feature-wrapper">';
2182 printf(
2183 '<input type="text" id="bot_message_font_color" name="mxchat_options[bot_message_font_color]" value="%s" class="my-color-field" data-default-color="#333333" %s />',
2184 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
2185 esc_attr($disabled)
2186 );
2187
2188 if (!$this->is_activated) {
2189 echo '<div class="pro-feature-overlay">';
2190 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2191 echo '</div>';
2192 }
2193
2194 echo '</div>';
2195 }
2196
2197 public function mxchat_top_bar_bg_color_callback() {
2198 $disabled = $this->is_activated ? '' : 'disabled';
2199
2200 echo '<div class="pro-feature-wrapper">';
2201 printf(
2202 '<input type="text" id="top_bar_bg_color" name="mxchat_options[top_bar_bg_color]" value="%s" class="my-color-field" data-default-color="#00b294" %s />',
2203 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
2204 esc_attr($disabled)
2205 );
2206
2207 if (!$this->is_activated) {
2208 echo '<div class="pro-feature-overlay">';
2209 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2210 echo '</div>';
2211 }
2212
2213 echo '</div>';
2214 }
2215
2216 public function mxchat_send_button_font_color_callback() {
2217 $disabled = $this->is_activated ? '' : 'disabled';
2218
2219 echo '<div class="pro-feature-wrapper">';
2220 printf(
2221 '<input type="text" id="send_button_font_color" name="mxchat_options[send_button_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2222 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
2223 esc_attr($disabled)
2224 );
2225
2226 if (!$this->is_activated) {
2227 echo '<div class="pro-feature-overlay">';
2228 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2229 echo '</div>';
2230 }
2231
2232 echo '</div>';
2233 }
2234
2235 public function mxchat_chatbot_background_color_callback() {
2236 $disabled = $this->is_activated ? '' : 'disabled';
2237
2238 echo '<div class="pro-feature-wrapper">';
2239 printf(
2240 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
2241 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
2242 esc_attr($disabled)
2243 );
2244
2245 if (!$this->is_activated) {
2246 echo '<div class="pro-feature-overlay">';
2247 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2248 echo '</div>';
2249 }
2250
2251 echo '</div>';
2252 }
2253
2254 public function mxchat_icon_color_callback() {
2255 $disabled = $this->is_activated ? '' : 'disabled';
2256
2257 echo '<div class="pro-feature-wrapper">';
2258 printf(
2259 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2260 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
2261 esc_attr($disabled)
2262 );
2263
2264 if (!$this->is_activated) {
2265 echo '<div class="pro-feature-overlay">';
2266 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2267 echo '</div>';
2268 }
2269
2270 echo '</div>';
2271 }
2272
2273 public function mxchat_chat_input_font_color_callback() {
2274 $disabled = $this->is_activated ? '' : 'disabled';
2275
2276 echo '<div class="pro-feature-wrapper">';
2277 printf(
2278 '<input type="text" id="chat_input_font_color" name="mxchat_options[chat_input_font_color]" value="%s" class="my-color-field" data-default-color="#555555" %s />',
2279 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
2280 esc_attr($disabled)
2281 );
2282
2283 if (!$this->is_activated) {
2284 echo '<div class="pro-feature-overlay">';
2285 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2286 echo '</div>';
2287 }
2288
2289 echo '</div>';
2290 }
2291
2292
2293 public function mxchat_append_to_body_callback() {
2294 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
2295 echo '<label class="toggle-switch">';
2296 printf(
2297 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
2298 esc_attr($append_to_body_checked)
2299 );
2300 echo '<span class="slider"></span>';
2301 echo '</label>';
2302 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>';
2303 }
2304
2305
2306 public function mxchat_privacy_toggle_callback() {
2307 // Check if the privacy toggle is enabled
2308 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
2309
2310 // Retrieve the stored privacy text if it exists
2311 $privacy_text = isset($this->options['privacy_text']) ? wp_kses_post($this->options['privacy_text']) : 'By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.';
2312
2313 // Output the toggle switch
2314 echo '<label class="toggle-switch">';
2315 printf(
2316 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
2317 esc_attr($privacy_toggle_checked)
2318 );
2319 echo '<span class="slider"></span>';
2320 echo '</label>';
2321 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
2322
2323 // Output the custom text input field
2324 printf(
2325 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
2326 esc_textarea($privacy_text)
2327 );
2328 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
2329 }
2330
2331
2332 public function mxchat_complianz_toggle_callback() {
2333 // Check if the Complianz toggle is enabled
2334 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
2335
2336 // Check if the plugin is activated (paid feature)
2337 $disabled = $this->is_activated ? '' : 'disabled';
2338 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2339
2340 echo '<div class="' . esc_attr($class) . '">';
2341
2342 // Output the toggle switch
2343 echo '<label class="toggle-switch">';
2344 printf(
2345 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
2346 esc_attr($complianz_toggle_checked),
2347 esc_attr($disabled)
2348 );
2349 echo '<span class="slider"></span>';
2350 echo '</label>';
2351 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot.</p>';
2352
2353 // If the feature is not activated, show the Pro feature overlay
2354 if (!$this->is_activated) {
2355 echo '<div class="pro-feature-overlay">';
2356 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2357 echo '</div>';
2358 }
2359
2360 echo '</div>';
2361 }
2362
2363
2364
2365
2366
2367 public function mxchat_link_target_toggle_callback() {
2368 // Check if the toggle is enabled in the options
2369 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
2370
2371 // Output the toggle switch
2372 echo '<label class="toggle-switch">';
2373 printf(
2374 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
2375 esc_attr($link_target_toggle)
2376 );
2377 echo '<span class="slider"></span>';
2378 echo '</label>';
2379 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
2380 }
2381
2382 public function mxchat_chat_persistence_toggle_callback() {
2383 // Check if chat persistence toggle is enabled
2384 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
2385
2386 // Check if the plugin is activated (paid feature)
2387 $disabled = $this->is_activated ? '' : 'disabled';
2388 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2389
2390 echo '<div class="' . esc_attr($class) . '">';
2391
2392 // Output the toggle switch
2393 echo '<label class="toggle-switch">';
2394 printf(
2395 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
2396 esc_attr($chat_persistence_toggle_checked),
2397 esc_attr($disabled)
2398 );
2399 echo '<span class="slider"></span>';
2400 echo '</label>';
2401 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
2402
2403 // If not activated, show Pro feature overlay
2404 if (!$this->is_activated) {
2405 echo '<div class="pro-feature-overlay">';
2406 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2407 echo '</div>';
2408 }
2409
2410 echo '</div>';
2411 }
2412
2413
2414 public function mxchat_popular_question_1_callback() {
2415 printf(
2416 '<input type="text" id="popular_question_1" name="mxchat_options[popular_question_1]" value="%s" placeholder="Enter Popular Question 1" />',
2417 isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
2418 );
2419 echo '<p class="description">This will be the first popular question in the chatbot.</p>';
2420 }
2421
2422 public function mxchat_popular_question_2_callback() {
2423 // Check if the plugin is activated (paid feature)
2424 $disabled = $this->is_activated ? '' : 'disabled';
2425 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2426
2427 echo '<div class="' . esc_attr($class) . '">';
2428
2429 printf(
2430 '<input type="text" id="popular_question_2" name="mxchat_options[popular_question_2]" value="%s" placeholder="Enter Popular Question 2" %s />',
2431 isset($this->options['popular_question_2']) ? esc_attr($this->options['popular_question_2']) : '',
2432 esc_attr($disabled)
2433 );
2434 echo '<p class="description">This will be the second popular question in the chatbot.</p>';
2435
2436 // If not activated, show Pro feature overlay
2437 if (!$this->is_activated) {
2438 echo '<div class="pro-feature-overlay">';
2439 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2440 echo '</div>';
2441 }
2442
2443 echo '</div>';
2444 }
2445
2446 public function mxchat_popular_question_3_callback() {
2447 // Check if the plugin is activated (paid feature)
2448 $disabled = $this->is_activated ? '' : 'disabled';
2449 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2450
2451 echo '<div class="' . esc_attr($class) . '">';
2452
2453 printf(
2454 '<input type="text" id="popular_question_3" name="mxchat_options[popular_question_3]" value="%s" placeholder="Enter Popular Question 3" %s />',
2455 isset($this->options['popular_question_3']) ? esc_attr($this->options['popular_question_3']) : '',
2456 esc_attr($disabled)
2457 );
2458 echo '<p class="description">This will be the third popular question in the chatbot.</p>';
2459
2460 // If not activated, show Pro feature overlay
2461 if (!$this->is_activated) {
2462 echo '<div class="pro-feature-overlay">';
2463 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2464 echo '</div>';
2465 }
2466
2467 echo '</div>';
2468 }
2469
2470
2471 /**
2472 * Callback for Brave API Key field.
2473 */
2474 public function mxchat_brave_api_key_callback() {
2475 $options = get_option('mxchat_options');
2476 $brave_api_key = isset($options['brave_api_key']) ? esc_attr($options['brave_api_key']) : '';
2477
2478 echo '<div class="api-key-wrapper">';
2479 printf(
2480 '<input type="password" id="brave_api_key" name="mxchat_options[brave_api_key]" value="%s" class="regular-text" />',
2481 $brave_api_key
2482 );
2483 echo '<button type="button" id="toggleBraveApiKeyVisibility" class="button-secondary">Show</button>';
2484 echo '</div>';
2485 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
2486
2487 ?>
2488 <?php
2489 }
2490
2491 /**
2492 * Callback for Number of Images to Return field.
2493 */
2494 public function mxchat_brave_image_count_callback() {
2495 $options = get_option('mxchat_options');
2496 $brave_image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
2497
2498 printf(
2499 '<input type="number" id="brave_image_count" name="mxchat_options[brave_image_count]" value="%d" min="1" max="6" />',
2500 $brave_image_count
2501 );
2502 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
2503 }
2504
2505 /**
2506 * Callback for Safe Search field.
2507 */
2508 public function mxchat_brave_safe_search_callback() {
2509 $options = get_option('mxchat_options');
2510 $brave_safe_search = isset($options['brave_safe_search']) ? esc_attr($options['brave_safe_search']) : 'strict';
2511
2512 ?>
2513 <select id="brave_safe_search" name="mxchat_options[brave_safe_search]">
2514 <option value="strict" <?php selected($brave_safe_search, 'strict'); ?>><?php _e('Strict', 'mxchat'); ?></option>
2515 <option value="off" <?php selected($brave_safe_search, 'off'); ?>><?php _e('Off', 'mxchat'); ?></option>
2516 </select>
2517 <p class="description"><?php _e('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat'); ?></p>
2518 <?php
2519 }
2520
2521
2522 /**
2523 * Callback for Number of News Articles field.
2524 */
2525 public function mxchat_brave_news_count_callback() {
2526 $options = get_option('mxchat_options');
2527 $brave_news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
2528
2529 printf(
2530 '<input type="number" id="brave_news_count" name="mxchat_options[brave_news_count]" value="%d" min="1" max="10" />',
2531 $brave_news_count
2532 );
2533 echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
2534 }
2535
2536 /**
2537 * Callback for Country field.
2538 */
2539 public function mxchat_brave_country_callback() {
2540 $options = get_option('mxchat_options');
2541 $brave_country = isset($options['brave_country']) ? esc_attr($options['brave_country']) : 'us';
2542
2543 printf(
2544 '<input type="text" id="brave_country" name="mxchat_options[brave_country]" value="%s" maxlength="2" class="small-text" />',
2545 $brave_country
2546 );
2547 echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
2548 }
2549
2550 /**
2551 * Callback for Language field.
2552 */
2553 public function mxchat_brave_language_callback() {
2554 $options = get_option('mxchat_options');
2555 $brave_language = isset($options['brave_language']) ? esc_attr($options['brave_language']) : 'en';
2556
2557 printf(
2558 '<input type="text" id="brave_language" name="mxchat_options[brave_language]" value="%s" maxlength="2" class="small-text" />',
2559 $brave_language
2560 );
2561 echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
2562 }
2563
2564
2565
2566
2567
2568 // Section Callback
2569 public function mxchat_pdf_intent_section_callback() {
2570 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
2571 }
2572
2573 // Intent Trigger Text Callback
2574 public function mxchat_pdf_intent_trigger_text_callback() {
2575 $disabled = $this->is_activated ? '' : 'disabled';
2576 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2577
2578 echo '<div class="' . esc_attr($class) . '">';
2579 printf(
2580 '<textarea id="pdf_intent_trigger_text" name="mxchat_options[pdf_intent_trigger_text]" rows="3" cols="50" placeholder="Enter trigger text" %s>%s</textarea>',
2581 esc_attr($disabled),
2582 isset($this->options['pdf_intent_trigger_text']) ? esc_textarea($this->options['pdf_intent_trigger_text']) : ''
2583 );
2584 echo '<p class="description">Text displayed when the intent is triggered.</p>';
2585
2586 if (!$this->is_activated) {
2587 echo '<div class="pro-feature-overlay">';
2588 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2589 echo '</div>';
2590 }
2591 echo '</div>';
2592 }
2593
2594 // Success Text Callback
2595 public function mxchat_pdf_intent_success_text_callback() {
2596 $disabled = $this->is_activated ? '' : 'disabled';
2597 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2598
2599 echo '<div class="' . esc_attr($class) . '">';
2600 printf(
2601 '<textarea id="pdf_intent_success_text" name="mxchat_options[pdf_intent_success_text]" rows="3" cols="50" placeholder="Enter success text" %s>%s</textarea>',
2602 esc_attr($disabled),
2603 isset($this->options['pdf_intent_success_text']) ? esc_textarea($this->options['pdf_intent_success_text']) : ''
2604 );
2605 echo '<p class="description">Text displayed when the intent is successful.</p>';
2606
2607 if (!$this->is_activated) {
2608 echo '<div class="pro-feature-overlay">';
2609 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2610 echo '</div>';
2611 }
2612 echo '</div>';
2613 }
2614
2615 // Error Text Callback
2616 public function mxchat_pdf_intent_error_text_callback() {
2617 $disabled = $this->is_activated ? '' : 'disabled';
2618 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2619
2620 echo '<div class="' . esc_attr($class) . '">';
2621 printf(
2622 '<textarea id="pdf_intent_error_text" name="mxchat_options[pdf_intent_error_text]" rows="3" cols="50" placeholder="Enter error text" %s>%s</textarea>',
2623 esc_attr($disabled),
2624 isset($this->options['pdf_intent_error_text']) ? esc_textarea($this->options['pdf_intent_error_text']) : ''
2625 );
2626 echo '<p class="description">Text displayed when an error occurs during the intent.</p>';
2627
2628 if (!$this->is_activated) {
2629 echo '<div class="pro-feature-overlay">';
2630 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2631 echo '</div>';
2632 }
2633 echo '</div>';
2634 }
2635
2636 // Maximum PDF Pages Callback
2637 public function mxchat_pdf_max_pages_callback() {
2638 $disabled = $this->is_activated ? '' : 'disabled';
2639 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2640 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
2641
2642 echo '<div class="' . esc_attr($class) . '">';
2643 printf(
2644 '<input type="range" id="pdf_max_pages" name="mxchat_options[pdf_max_pages]" min="1" max="69" value="%d" %s oninput="document.getElementById(\'pdf_max_pages_output\').innerText = this.value;" />',
2645 esc_attr($max_pages),
2646 esc_attr($disabled)
2647 );
2648 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
2649 echo '<p class="description">Set the maximum number of PDF pages users can upload for processing. (1-69 pages)</p>';
2650
2651 if (!$this->is_activated) {
2652 echo '<div class="pro-feature-overlay">';
2653 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2654 echo '</div>';
2655 }
2656 echo '</div>';
2657 }
2658
2659
2660
2661
2662 public function mxchat_enqueue_admin_assets() {
2663 wp_enqueue_style('wp-color-picker');
2664
2665 // Get the plugin version or file modification time for cache busting
2666 $plugin_version = '1.4.5'; // Replace this with your plugin's version
2667
2668 // File paths
2669 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
2670 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
2671 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
2672 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
2673
2674 // Check if files exist and get modification times
2675 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
2676 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
2677 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
2678 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
2679
2680 // Enqueue scripts and styles with corrected paths
2681 wp_enqueue_script(
2682 'mxchat-color-picker',
2683 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
2684 array('wp-color-picker'),
2685 $color_picker_version,
2686 true
2687 );
2688
2689 wp_enqueue_script(
2690 'mxchat-embedding-check',
2691 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
2692 array(),
2693 $embedding_check_version,
2694 true
2695 );
2696
2697 wp_enqueue_script(
2698 'mxchat-transcripts-js',
2699 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
2700 array('jquery'),
2701 $transcripts_js_version,
2702 true
2703 );
2704
2705 wp_enqueue_script(
2706 'mxchat-admin-js',
2707 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
2708 array('jquery'),
2709 $plugin_version,
2710 true
2711 );
2712
2713
2714
2715 // Localize the script with data for the activation process
2716 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
2717 'ajax_url' => admin_url('admin-ajax.php'),
2718 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
2719 ));
2720
2721 wp_enqueue_style(
2722 'mxchat-admin-css',
2723 plugin_dir_url(__FILE__) . '../css/admin-style.css',
2724 array(),
2725 $admin_css_version
2726 );
2727
2728 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
2729 'ajax_url' => admin_url('admin-ajax.php'),
2730 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
2731 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
2732 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
2733 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
2734 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
2735 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
2736 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
2737 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
2738 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
2739 'icon_color' => $this->options['icon_color'] ?? '#fff',
2740 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
2741 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
2742 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
2743
2744 // New fields for Loops Integration
2745 'loops_api_key' => $this->options['loops_api_key'] ?? '',
2746 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
2747 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
2748 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.',
2749 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.",
2750 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?",
2751 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
2752 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
2753
2754 ));
2755
2756 // Localize the script to pass the nonce and other data to JavaScript
2757 wp_localize_script('mxchat-admin-js', 'mxchatInlineEdit', array(
2758 'nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
2759 'ajax_url' => admin_url('admin-ajax.php')
2760 ));
2761
2762
2763 }
2764
2765 public function mxchat_sanitize($input) {
2766 $new_input = array();
2767
2768 if (isset($input['api_key'])) {
2769 $new_input['api_key'] = sanitize_text_field($input['api_key']);
2770 }
2771
2772 if (isset($input['xai_api_key'])) {
2773 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
2774 }
2775
2776 if (isset($input['claude_api_key'])) {
2777 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
2778 }
2779
2780 if (isset($input['enable_woocommerce_integration'])) {
2781 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
2782
2783 }
2784
2785 if (isset($input['privacy_toggle'])) {
2786 $new_input['privacy_toggle'] = $input['privacy_toggle'];
2787 }
2788
2789 if (isset($input['complianz_toggle'])) {
2790 $new_input['complianz_toggle'] = $input['complianz_toggle'];
2791 }
2792
2793 // Handle custom privacy text input
2794 if (isset($input['privacy_text'])) {
2795 // Allow basic HTML for links
2796 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
2797 }
2798
2799 if (isset($input['system_prompt_instructions'])) {
2800 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
2801 }
2802
2803 if (isset($input['mxchat_pro_email'])) {
2804 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
2805 }
2806
2807 if (isset($input['mxchat_activation_key'])) {
2808 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
2809 }
2810
2811 if (isset($input['append_to_body'])) {
2812 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
2813 }
2814
2815 if (isset($input['top_bar_title'])) {
2816 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
2817 }
2818
2819 if (isset($input['intro_message'])) {
2820 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
2821 }
2822
2823 if (isset($input['input_copy'])) {
2824 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
2825 }
2826
2827
2828 if (isset($input['rate_limit_message'])) {
2829 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
2830 }
2831
2832 if (isset($input['rate_limit_logged_in'])) {
2833 $allowed_limits = array('1', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
2834 $rate_limit = sanitize_text_field($input['rate_limit_logged_in']);
2835
2836 if (in_array($rate_limit, $allowed_limits, true)) {
2837 $new_input['rate_limit_logged_in'] = $rate_limit;
2838 } else {
2839 $new_input['rate_limit_logged_in'] = '100'; // Default for logged-in users
2840 }
2841 }
2842
2843 if (isset($input['rate_limit_logged_out'])) {
2844 $allowed_limits = array('1', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
2845 $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
2846
2847 if (in_array($rate_limit, $allowed_limits, true)) {
2848 $new_input['rate_limit_logged_out'] = $rate_limit;
2849 } else {
2850 $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
2851 }
2852 }
2853
2854 if (isset($input['pre_chat_message'])) {
2855 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
2856 }
2857
2858 if (isset($input['model'])) {
2859 $allowed_models = array(
2860 'grok-beta',
2861 'claude-3-5-sonnet-20241022',
2862 'claude-3-opus-20240229',
2863 'claude-3-sonnet-20240229',
2864 'claude-3-haiku-20240307',
2865 'gpt-4o',
2866 'gpt-4o-mini',
2867 'gpt-4-turbo',
2868 'gpt-4',
2869 'gpt-3.5-turbo',
2870 );
2871 if (in_array($input['model'], $allowed_models)) {
2872 $new_input['model'] = sanitize_text_field($input['model']);
2873 }
2874 }
2875
2876 // Sanitize new pro features
2877 if (isset($input['close_button_color'])) {
2878 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
2879 }
2880
2881 if (isset($input['chatbot_bg_color'])) {
2882 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
2883 }
2884
2885 if (isset($input['woocommerce_consumer_key'])) {
2886 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
2887 }
2888
2889 if (isset($input['woocommerce_consumer_secret'])) {
2890 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
2891 }
2892
2893 if (isset($input['user_message_bg_color'])) {
2894 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
2895 }
2896
2897 if (isset($input['user_message_font_color'])) {
2898 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
2899 }
2900
2901 if (isset($input['bot_message_bg_color'])) {
2902 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
2903 }
2904
2905 if (isset($input['bot_message_font_color'])) {
2906 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
2907 }
2908
2909 if (isset($input['top_bar_bg_color'])) {
2910 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
2911 }
2912
2913 if (isset($input['send_button_font_color'])) {
2914 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
2915 }
2916
2917 if (isset($input['chatbot_background_color'])) {
2918 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
2919 }
2920
2921 if (isset($input['icon_color'])) {
2922 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
2923 }
2924
2925 if (isset($input['chat_input_font_color'])) {
2926 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
2927 }
2928
2929 // Sanitize link_target_toggle
2930 if (isset($input['link_target_toggle'])) {
2931 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
2932 }
2933
2934 // Sanitize Loops API Key
2935 if (isset($input['loops_api_key'])) {
2936 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
2937 }
2938
2939 if (isset($input['chat_persistence_toggle'])) {
2940 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
2941 }
2942
2943
2944
2945 if (isset($input['popular_question_1'])) {
2946 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
2947 }
2948
2949 if (isset($input['popular_question_2'])) {
2950 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
2951 }
2952
2953 if (isset($input['popular_question_3'])) {
2954 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
2955 }
2956
2957
2958 // Sanitize Loops Mailing List
2959 if (isset($input['loops_mailing_list'])) {
2960 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
2961 }
2962
2963 // Sanitize Triggered Phrase Response
2964 if (isset($input['triggered_phrase_response'])) {
2965 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
2966 }
2967
2968 if (isset($input['email_capture_response'])) {
2969 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
2970 }
2971
2972
2973 // Sanitize Brave Search Settings
2974 if (isset($input['brave_api_key'])) {
2975 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
2976 }
2977
2978 if (isset($input['brave_image_count'])) {
2979 $image_count = intval($input['brave_image_count']);
2980 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
2981 }
2982
2983 if (isset($input['brave_safe_search'])) {
2984 $allowed = array('strict', 'off');
2985 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
2986 }
2987
2988 if (isset($input['brave_news_count'])) {
2989 $news_count = intval($input['brave_news_count']);
2990 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
2991 }
2992
2993 if (isset($input['brave_country'])) {
2994 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
2995 }
2996
2997 if (isset($input['brave_language'])) {
2998 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
2999 }
3000
3001
3002
3003 if (isset($input['pdf_intent_trigger_text'])) {
3004 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
3005 }
3006
3007 if (isset($input['pdf_intent_success_text'])) {
3008 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
3009 }
3010
3011 if (isset($input['pdf_intent_error_text'])) {
3012 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
3013 }
3014
3015 if (isset($input['pdf_max_pages'])) {
3016 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
3017 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
3018 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
3019 }
3020 }
3021
3022
3023 return $new_input;
3024 }
3025
3026
3027 // Method to append the chatbot to the body
3028 public function mxchat_append_chatbot_to_body() {
3029 $options = get_option('mxchat_options');
3030 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
3031 echo do_shortcode('[mxchat_chatbot floating="yes"]');
3032 }
3033 }
3034
3035
3036
3037 private function mxchat_extract_main_content($html) {
3038 $dom = new DOMDocument;
3039 libxml_use_internal_errors(true); // Suppress HTML parsing errors
3040 @$dom->loadHTML($html);
3041 libxml_clear_errors();
3042
3043 $xpath = new DOMXPath($dom);
3044
3045 // Simplified selectors focusing on common content areas
3046 $selectors = [
3047 '//article',
3048 '//*[@id="content"]',
3049 '//*[@class="entry-content"]',
3050 '//main',
3051 ];
3052
3053 foreach ($selectors as $selector) {
3054 $nodes = $xpath->query($selector);
3055 if ($nodes->length > 0) {
3056 $content = '';
3057 foreach ($nodes as $node) {
3058 $content .= $dom->saveHTML($node);
3059 }
3060 return $content;
3061 }
3062 }
3063
3064 // Fallback: Return the entire body content if no specific selector matches
3065 $body = $dom->getElementsByTagName('body');
3066 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
3067 }
3068
3069 public function mxchat_handle_sitemap_submission() {
3070 // Check if the form was submitted and the user has sufficient permissions
3071 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
3072 return;
3073 }
3074
3075 // Verify the nonce field for security
3076 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
3077
3078 // Sanitize and retrieve the submitted URL
3079 $submitted_url = esc_url_raw($_POST['sitemap_url']); // Accept either a sitemap URL or a regular URL
3080
3081 // Fetch the content of the submitted URL
3082 $response = wp_remote_get($submitted_url);
3083 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
3084 set_transient('mxchat_admin_notice_error', 'Failed to fetch the URL. Please check the URL and try again.', 30);
3085 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3086 exit;
3087 }
3088
3089 $content_type = wp_remote_retrieve_header($response, 'content-type');
3090 $body_content = wp_remote_retrieve_body($response);
3091
3092 // Check if the URL points to a sitemap (XML) or a regular HTML page
3093 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
3094 // Handle Sitemap XML
3095 $xml = simplexml_load_string($body_content);
3096 if ($xml === false) {
3097 set_transient('mxchat_admin_notice_error', 'Invalid sitemap XML. Please provide a valid sitemap.', 30);
3098 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3099 exit;
3100 }
3101
3102 $embedding_success = true; // Flag to track if all embeddings are successful
3103
3104 foreach ($xml->url as $url_element) {
3105 $page_url = (string)$url_element->loc;
3106
3107 $page_response = wp_remote_get($page_url);
3108 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
3109 continue;
3110 }
3111
3112 $page_html = wp_remote_retrieve_body($page_response);
3113 $page_content = $this->mxchat_extract_main_content($page_html);
3114 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
3115
3116 if (!empty($sanitized_content)) {
3117 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
3118 if (is_array($embedding_vector)) {
3119 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $this->options['api_key']);
3120 } else {
3121 $embedding_success = false; // Set flag to false if any embedding fails
3122 }
3123 }
3124 }
3125
3126 if ($embedding_success) {
3127 set_transient('mxchat_admin_notice_success', 'Sitemap content successfully submitted!', 30);
3128 } else {
3129 set_transient('mxchat_admin_notice_error', 'Some content failed to embed. Please check your API key and try again.', 30);
3130 }
3131
3132 } else {
3133 // Handle Regular URL (HTML Page)
3134 $page_content = $this->mxchat_extract_main_content($body_content);
3135 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
3136
3137 if (!empty($sanitized_content)) {
3138 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
3139 if (is_array($embedding_vector)) {
3140 MxChat_Utils::submit_content_to_db($sanitized_content, $submitted_url, $this->options['api_key']);
3141 set_transient('mxchat_admin_notice_success', 'URL content successfully submitted!', 30);
3142 } else {
3143 set_transient('mxchat_admin_notice_error', 'Failed to generate embedding for the URL content. Please check your API key and try again.', 30);
3144 }
3145 } else {
3146 set_transient('mxchat_admin_notice_error', 'No valid content found on the provided URL.', 30);
3147 }
3148 }
3149
3150 // Redirect after setting the transient
3151 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3152 exit;
3153 }
3154
3155
3156
3157 private function mxchat_sanitize_content_for_api($content) {
3158 // Remove script, style tags, and HTML comments
3159 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
3160 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
3161 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
3162
3163 // Remove all HTML tags and decode HTML entities
3164 $content = wp_strip_all_tags($content);
3165 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
3166
3167 // Trim and normalize whitespace
3168 $content = trim(preg_replace('/\s+/', ' ', $content));
3169
3170 return $content;
3171 }
3172
3173
3174
3175 private function mxchat_fetch_loops_mailing_lists($api_key) {
3176 $url = 'https://app.loops.so/api/v1/lists';
3177 $response = wp_remote_get($url, array(
3178 'headers' => array(
3179 'Authorization' => 'Bearer ' . $api_key,
3180 'Content-Type' => 'application/json'
3181 )
3182 ));
3183
3184 if (is_wp_error($response)) {
3185 return array();
3186 }
3187
3188 $body = wp_remote_retrieve_body($response);
3189 $lists = json_decode($body, true);
3190
3191 return isset($lists) && is_array($lists) ? $lists : array();
3192 }
3193
3194
3195
3196
3197
3198 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
3199 if (empty($vec1) || empty($vec2)) {
3200 return 0.0;
3201 }
3202
3203 $dot_product = 0.0;
3204 $norm_a = 0.0;
3205 $norm_b = 0.0;
3206
3207 for ($i = 0; $i < count($vec1); $i++) {
3208 $dot_product += $vec1[$i] * $vec2[$i];
3209 $norm_a += pow($vec1[$i], 2);
3210 $norm_b += pow($vec2[$i], 2);
3211 }
3212
3213 if ($norm_a == 0.0 || $norm_b == 0.0) {
3214 return 0.0;
3215 } else {
3216 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
3217 }
3218 }
3219
3220
3221
3222
3223 }
3224 ?>
3225