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

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

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