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

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