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

2,180 lines 87.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
37
38
39 }
40
41 // Method to check if the license is active
42 private function is_license_active() {
43 $license_status = get_option('mxchat_license_status', 'inactive');
44 return $license_status === 'active';
45 }
46
47 // Initialize default options
48 private function initialize_default_options() {
49 $default_options = array(
50 'api_key' => '',
51 '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:
52 - Your name is [Chatbot Name]. Always introduce yourself as this name when appropriate.
53 - 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.
54 - When appropriate, highlight the benefits of [insert proper subject here]. Offer to guide visitors to relevant pages or provide them with more information.
55 - 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.
56 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
57 - 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.
58 - 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.',
59 'model' => 'gpt-3.5-turbo',
60 'rate_limit' => '100',
61 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
62 'top_bar_title' => 'MxChat',
63 'intro_message' => 'Hello! How can I assist you today?',
64 'input_copy' => 'How can I assist?',
65 'append_to_body' => 'off',
66 'close_button_color' => '#fff',
67 'chatbot_bg_color' => '#fff',
68 'user_message_bg_color' => '#fff',
69 'user_message_font_color' => '#212121',
70 'bot_message_bg_color' => '#212121',
71 'bot_message_font_color' => '#fff',
72 'top_bar_bg_color' => '#212121',
73 'send_button_font_color' => '#212121',
74 'chat_input_font_color' => '#212121',
75 'chatbot_background_color' => '#212121',
76 'icon_color' => '#fff',
77 'enable_woocommerce_integration' => '0',
78 'enable_woocommerce_order_access' => '0',
79 'link_target_toggle' => 'off',
80 'pre_chat_message' => 'Hey there! Ask me anything!',
81
82 // New fields for Loops Integration
83 'loops_api_key' => '',
84 'loops_mailing_list' => '',
85 'trigger_keywords' => '',
86 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
87 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
88 );
89
90
91 // Merge existing options with defaults
92 $existing_options = get_option('mxchat_options', array());
93 $merged_options = wp_parse_args($existing_options, $default_options);
94
95 // Update the options if they have changed
96 if ($existing_options !== $merged_options) {
97 update_option('mxchat_options', $merged_options);
98 }
99
100 // Update the $this->options property
101 $this->options = $merged_options;
102 }
103
104
105
106 public function mxchat_add_plugin_page() {
107 // Main menu page
108 add_menu_page(
109 'MxChat Settings',
110 'MxChat',
111 'manage_options',
112 'mxchat-max',
113 array($this, 'mxchat_create_admin_page'),
114 'dashicons-testimonial',
115 6
116 );
117
118 // Submenu page for Knowledge
119 add_submenu_page(
120 'mxchat-max',
121 'Prompts',
122 'Knowledge',
123 'manage_options',
124 'mxchat-prompts',
125 array($this, 'mxchat_create_prompts_page')
126 );
127
128 // Submenu page for Chat Transcripts
129 add_submenu_page(
130 'mxchat-max', // Corrected parent slug to match the main menu
131 'Chat Transcripts',
132 'Transcripts',
133 'manage_options',
134 'mxchat-transcripts',
135 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
136 );
137
138 // Submenu page for Activation Key
139 add_submenu_page(
140 'mxchat-max',
141 'Pro Upgrade',
142 'Pro Upgrade',
143 'manage_options',
144 'mxchat-activation',
145 array($this, 'mxchat_create_activation_page')
146 );
147 }
148
149
150 public function mxchat_handle_content_submission() {
151 // Check if the form was submitted and the user has sufficient permissions
152 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
153 return;
154 }
155
156 // Verify the nonce field for security
157 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
158 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
159 wp_die('Nonce verification failed.');
160 }
161
162 // Sanitize the content input
163 $article_content = sanitize_textarea_field($_POST['article_content']);
164
165 // Sanitize the URL input (optional URL)
166 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
167
168 // Generate the embedding vector for the content
169 $embedding_vector = $this->mxchat_generate_embedding($article_content);
170
171 global $wpdb;
172 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
173
174 // Check if the 'source_url' column exists in the table and add it if it doesn't
175 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
176 // Use wpdb::query and a prepared statement to avoid SQL injection
177 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
178 }
179
180 if (is_array($embedding_vector)) {
181 // Serialize the embedding vector before storing it
182 $embedding_vector_serialized = serialize($embedding_vector);
183
184 // Insert the content, embedding vector, and source URL into the database, using a prepared statement
185 $inserted = $wpdb->insert(
186 $table_name,
187 array(
188 'article_content' => $article_content,
189 'embedding_vector' => $embedding_vector_serialized,
190 'source_url' => $article_url, // Insert the URL, empty string if not provided
191 ),
192 array(
193 '%s', // Format for article_content (string)
194 '%s', // Format for embedding_vector (serialized string)
195 '%s', // Format for source_url (string)
196 )
197 );
198
199 if ($inserted === false) {
200 //error_log('Error inserting content: ' . $wpdb->last_error);
201 set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
202 } else {
203 set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
204 }
205
206 } else {
207 //error_log('Embedding generation failed for article content: ' . $article_content);
208 set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
209 }
210
211 // Redirect after setting the transient
212 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
213 exit;
214 }
215
216 public function mxchat_display_admin_notice() {
217 // Success notice
218 if ($message = get_transient('mxchat_admin_notice_success')) {
219 ?>
220 <div class="notice notice-success is-dismissible">
221 <p><?php echo esc_html($message); ?></p>
222 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
223 </div>
224 <?php
225 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
226 }
227
228 // Error notice
229 if ($message = get_transient('mxchat_admin_notice_error')) {
230 ?>
231 <div class="notice notice-error is-dismissible">
232 <p><?php echo esc_html($message); ?></p>
233 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
234 </div>
235 <?php
236 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
237 }
238 }
239
240
241 public function mxchat_handle_delete_prompt() {
242 // Sanitize and validate nonce using wp_unslash and sanitize_text_field
243 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
244 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
245 wp_die('Nonce verification failed.');
246 }
247
248 // Check user permissions
249 if (!current_user_can('manage_options')) {
250 wp_die('You do not have sufficient permissions to delete prompts.');
251 }
252
253 // Sanitize and validate the 'id' parameter
254 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
255 if ($id <= 0) {
256 wp_die('Invalid prompt ID.');
257 }
258
259 global $wpdb;
260 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
261
262 // Clear relevant cache before deletion
263 $cache_key = 'prompt_' . $id;
264 wp_cache_delete($cache_key, 'mxchat_prompts');
265
266 // Delete the record using a prepared statement
267 $deleted = $wpdb->delete($table_name, array('id' => $id), array('%d'));
268
269 if ($deleted !== false) {
270 // Optionally, clear a general cache if you have one
271 wp_cache_delete('all_prompts', 'mxchat_prompts');
272 }
273
274 // Redirect to the prompts page with a success message
275 $redirect_url = add_query_arg(array(
276 'page' => 'mxchat-prompts',
277 'deleted' => 'true'
278 ), admin_url('admin.php'));
279
280 wp_safe_redirect($redirect_url);
281 exit;
282 }
283
284
285 public function mxchat_create_admin_page() {
286 ?>
287 <div class="wrap mxchat-admin">
288 <?php if (!$this->is_activated): ?>
289 <div class="mxchat-pro-banner">
290 <p>
291 Want even more features such as a fully customizable theme, WooCommerce integration, dedicated support, and much more?
292 <a href="https://mxchat.ai/" target="_blank">Upgrade to MxChat Pro today and get a special launch lifetime license for only $49.97!</a>
293 </p>
294 </div>
295 <?php endif; ?>
296 <h2 class="admin-title">Mx<span class="admin-emphasis">Chat</span></h2>
297 <h2 class="mxchat-nav-tab-wrapper">
298 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
299 <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
300 <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
301 <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
302 </h2>
303 <form method="post" action="options.php">
304 <?php settings_fields('mxchat_option_group'); ?>
305 <div id="chatbot" class="mxchat-tab-content active">
306 <?php do_settings_sections('mxchat-chatbot'); ?>
307 </div>
308
309
310
311 <div id="embed" class="mxchat-tab-content">
312 <div class="mxchat-settings-section">
313 <h2>WooCommerce Settings</h2>
314 <table class="form-table">
315 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
316 </table>
317 </div>
318
319 <div class="section-divider"></div> <!-- Divider -->
320
321 <div class="mxchat-settings-section">
322 <h2>Loops Settings</h2>
323 <table class="form-table">
324 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
325 </table>
326 </div>
327 </div>
328
329
330
331
332
333 <div id="theme" class="mxchat-tab-content">
334 <?php do_settings_sections('mxchat-theme'); ?>
335 </div>
336 <div id="general" class="mxchat-tab-content">
337 <?php do_settings_sections('mxchat-general'); ?>
338 <p>If you're having trouble setting up or getting the responses you need, please don’t hesitate to <a href="https://mxchat.ai/contact/">contact our team</a>.<p> <div class="faq-item">
339 <h3>How do I add the chatbot to my site?</h3>
340 <p>
341 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.
342 </p>
343 </div>
344
345
346 <div class="faq-item">
347 <h3>Do I need an API key to use the chatbot?</h3>
348 <p>
349 Yes, you need an OpenAI API key to use this chatbot. You can obtain an API key from the <a href="https://platform.openai.com/signup" target="_blank" rel="noopener noreferrer">OpenAI website</a>. After signing up, you will need to add credits to your account, and generally, $5 in credits is enough to start using the chatbot.
350 </p>
351 </div>
352
353 <div class="faq-item">
354 <h3>How does the chatbot use my content to generate responses?</h3>
355 <p>
356 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.
357 </p>
358 </div>
359
360 <div class="faq-item">
361 <h3>Why does the chatbot sometimes make up links or information?</h3>
362 <p>
363 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.
364 </p>
365 </div>
366
367 <div class="faq-item">
368 <h3>How does the Complianz integration work?</h3>
369 <p>
370 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.
371 </p>
372 </div>
373
374 <div class="faq-item">
375 <h3>How does the WooCommerce integration work?</h3>
376 <p>
377 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.
378 </p>
379 </div>
380
381 <div class="faq-item">
382 <h3>Why isn't my chatbot responding as expected?</h3>
383 <p>
384 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.
385 </p>
386 <p>
387 We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
388 </p>
389 </div>
390
391 <div class="faq-item">
392 <h3>What is Loops, and how do I get an API key?</h3>
393 <p>
394 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.
395 </p>
396 </div>
397
398
399 </div>
400 <?php submit_button(); ?>
401 </form>
402 </div>
403 <?php
404 }
405
406
407 public function mxchat_create_transcripts_page() {
408 ?>
409 <div class="wrap mxchat-admin">
410 <h2><?php esc_html_e('Chat Transcripts', 'mxchat'); ?></h2>
411 <form id="mxchat-delete-form" method="post">
412 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
413 <div class="mxchat-controls">
414 <label for="mxchat-select-all-transcripts" class="mxchat-select-all-label">
415 <input type="checkbox" id="mxchat-select-all-transcripts" /> <?php esc_html_e('Select All', 'mxchat'); ?>
416 </label>
417 <input type="submit" value="<?php esc_attr_e('Delete Selected', 'mxchat'); ?>" class="button button-primary delete-chats-button" />
418 </div>
419 <div id="mxchat-transcripts">
420 <!-- Transcripts will be loaded here -->
421 </div>
422 </form>
423 </div>
424 <?php
425 }
426
427
428
429
430 public function mxchat_create_prompts_page() {
431 global $wpdb;
432 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
433
434 // Verify the nonce before processing the request
435 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
436
437 if (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce')) {
438 // Sanitize input data
439 $search_query = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : '';
440 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
441 } else {
442 $search_query = '';
443 $current_page = 1;
444 }
445
446 $per_page = 10;
447
448 // Create cache keys
449 $cache_key_total = 'total_prompts_' . md5($search_query);
450 $cache_key_prompts = 'prompts_' . md5($search_query . '_' . $current_page);
451
452 // Retrieve total number of prompts from cache or database
453 $total_prompts = wp_cache_get($cache_key_total, 'mxchat_prompts');
454 if ($total_prompts === false) {
455 if (!empty($search_query)) {
456 $total_prompts = $wpdb->get_var(
457 $wpdb->prepare(
458 "SELECT COUNT(*) FROM {$table_name} WHERE article_content LIKE %s",
459 '%' . $wpdb->esc_like($search_query) . '%'
460 )
461 );
462 } else {
463 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");
464 }
465 wp_cache_set($cache_key_total, $total_prompts, 'mxchat_prompts', 3600); // Cache for 1 hour
466 }
467
468 $total_pages = ceil($total_prompts / $per_page);
469 $offset = ($current_page - 1) * $per_page;
470
471 // Retrieve prompts from cache or database
472 $prompts = wp_cache_get($cache_key_prompts, 'mxchat_prompts');
473 if ($prompts === false) {
474 if (!empty($search_query)) {
475 $prompts = $wpdb->get_results(
476 $wpdb->prepare(
477 "SELECT * FROM {$table_name} WHERE article_content LIKE %s ORDER BY timestamp DESC LIMIT %d OFFSET %d",
478 '%' . $wpdb->esc_like($search_query) . '%',
479 $per_page,
480 $offset
481 )
482 );
483 } else {
484 $prompts = $wpdb->get_results(
485 $wpdb->prepare(
486 "SELECT * FROM {$table_name} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
487 $per_page,
488 $offset
489 )
490 );
491 }
492 wp_cache_set($cache_key_prompts, $prompts, 'mxchat_prompts', 3600); // Cache for 1 hour
493 }
494
495 ?>
496
497 <div class="wrap mxchat-admin">
498 <div class="mxchat-grid-container">
499
500 <!-- Submit Content -->
501 <div class="mxchat-grid-item full-width">
502 <h2>Submit Content</h2>
503 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
504 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
505 <div class="mxchat-form-group">
506 <label for="article_content">Article Content:</label>
507 <textarea name="article_content" id="article_content" required></textarea>
508 </div>
509 <div class="mxchat-form-group">
510 <label for="article_url">Article URL (Optional):</label>
511 <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
512 </div>
513 <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
514 </form>
515 </div>
516
517
518 <!-- Submit Sitemap -->
519 <div class="mxchat-grid-item">
520 <h2>Submit Sitemap or Page URL</h2>
521 <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')); ?>">
522 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
523 <div class="mxchat-search-group">
524 <input type="url" name="sitemap_url" id="sitemap_url" placeholder="Sitemap or URL" required />
525 <input type="submit" name="submit_sitemap" value="Submit" class="button button-primary" />
526 </div>
527 </form>
528 <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"></div>
529 <div id="mxchat-loading-text" style="display: none; margin-top: 10px; color: #333;">Loading sitemap content into database, please wait...</div>
530 </div>
531
532 <!-- Search Knowledge -->
533 <div class="mxchat-grid-item">
534 <h2>Search Knowledge</h2>
535 <form method="get" id="knowledge-search">
536 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
537 <input type="hidden" name="page" value="mxchat-prompts" />
538 <div class="mxchat-search-group">
539 <input type="text" name="search" placeholder="Search Knowledge" value="<?php echo esc_attr($search_query); ?>" />
540 <input type="submit" value="Search" class="button button-primary" />
541 </div>
542 </form>
543 </div>
544 </div>
545
546 <!-- Table below the forms -->
547 <div class="tablenav">
548 <div class="tablenav-pages">
549 <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
550 <?php
551 $page_links = paginate_links(array(
552 'base' => add_query_arg('paged', '%#%', 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 'add_args' => array(
559 'search' => $search_query,
560 '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
561 ),
562 ));
563
564 if ($page_links) {
565 echo '<div class="tablenav-pages">' . wp_kses_post($page_links) . '</div>';
566 }
567 ?>
568 </div>
569 </div>
570 <table class="wp-list-table widefat fixed striped">
571 <thead>
572 <tr>
573 <th>ID</th>
574 <th>Article Content</th>
575 <th>URL</th>
576 <th>Actions</th>
577 </tr>
578 </thead>
579 <tbody>
580 <?php
581 if ($prompts) {
582 foreach ($prompts as $prompt) {
583 ?>
584 <tr>
585 <td><?php echo esc_html($prompt->id); ?></td>
586 <td class="mxchat_article_content_dashboard"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></td>
587 <td class="mxchat_article_url_dashboard">
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 </td>
594 <td>
595 <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'))); ?>">Delete</a>
596 </td>
597 </tr>
598 <?php
599 }
600 } else {
601 ?>
602 <tr>
603 <td colspan="4">No prompts found.</td>
604 </tr>
605 <?php
606 }
607 ?>
608 </tbody>
609 </table>
610 </div>
611
612 <?php
613 }
614
615 public function mxchat_generate_embedding($text) {
616 $options = get_option('mxchat_options');
617 $api_key = $options['api_key'] ?? 'default_api_key';
618
619 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
620 'body' => wp_json_encode(array(
621 'model' => 'text-embedding-ada-002',
622 'input' => $text
623 )),
624 'headers' => array(
625 'Authorization' => 'Bearer ' . $api_key,
626 'Content-Type' => 'application/json'
627 ),
628 ));
629
630 if (is_wp_error($response)) {
631 return null;
632 }
633
634 $response_data = json_decode(wp_remote_retrieve_body($response), true);
635 return $response_data['data'][0]['embedding'] ?? null;
636 }
637
638 public function mxchat_delete_chat_history() {
639 if (!current_user_can('manage_options')) {
640 echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
641 wp_die();
642 }
643
644 check_ajax_referer('mxchat_delete_chat_history', 'security');
645
646 global $wpdb;
647 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
648
649 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
650 foreach ($_POST['delete_session_ids'] as $session_id) {
651 $session_id_sanitized = sanitize_text_field($session_id);
652
653 // Clear relevant cache before deletion
654 $cache_key = 'chat_session_' . $session_id_sanitized;
655 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
656
657 // Perform the deletion
658 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
659 }
660
661 // Optionally, clear a general cache if you have one
662 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
663
664 echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
665 } else {
666 echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
667 }
668
669 wp_die();
670 }
671
672
673
674 public function mxchat_fetch_chat_history() {
675 global $wpdb;
676 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
677
678 // Check if the current user has sufficient permissions
679 if (!current_user_can('manage_options')) {
680 echo esc_html__('You do not have sufficient permissions to view this page.', 'mxchat');
681 wp_die();
682 }
683
684 // Fetch chat transcripts from the database, ordered by timestamp
685 $chat_transcripts = $wpdb->get_results(
686 $wpdb->prepare("SELECT * FROM {$table_name} ORDER BY timestamp ASC")
687 );
688
689 // If no transcripts are available, display a message
690 if (empty($chat_transcripts)) {
691 echo esc_html__('No chat history available.', 'mxchat');
692 wp_die();
693 }
694
695 ob_start();
696 $current_session_id = '';
697
698 foreach ($chat_transcripts as $transcript) {
699 // Start a new session block if session ID changes
700 if ($current_session_id !== $transcript->session_id) {
701 if ($current_session_id !== '') {
702 echo '</div>'; // Close the previous session block
703 }
704 $current_session_id = sanitize_text_field($transcript->session_id);
705 echo '<div class="chat-session">';
706 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>';
707 }
708
709 // Format the timestamp for display
710 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
711
712 // Determine the role to display (user identifier or email for users, bot for the AI)
713 $role = $transcript->role;
714 if ($role === 'user') {
715 // Sanitize email if available
716 if (!empty($transcript->user_email)) {
717 $role = sanitize_email($transcript->user_email);
718 } else {
719 // Sanitize user identifier, anonymize if it's an IP address
720 $user_identifier = sanitize_text_field($transcript->user_identifier);
721
722 // Check if the user identifier is an IP address and anonymize it
723 if (filter_var($user_identifier, FILTER_VALIDATE_IP)) {
724 $role = preg_replace('/\.\d+$/', '.xxx', $user_identifier); // Mask the last octet
725 } else {
726 $role = $user_identifier; // If it's not an IP, just display the identifier
727 }
728 }
729 }
730
731 // Output the chat message
732 echo '<div class="chat-message">';
733 echo '<strong>' . esc_html($role) . ' (' . esc_html($formatted_timestamp) . '):</strong> ';
734 echo wp_kses_post($transcript->message);
735 echo '</div>';
736 }
737
738 // Close the final session block
739 echo '</div>';
740
741 $output = ob_get_clean();
742
743 echo $output;
744 wp_die();
745 }
746
747
748 public function mxchat_create_activation_page() {
749 $license_status = get_option('mxchat_license_status', 'inactive');
750 $license_error = get_option('mxchat_license_error', '');
751
752 ?>
753 <div class="wrap mxchat-admin">
754 <h2>MxChat Pro: Activation</h2>
755 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
756 <div class="error notice">
757 <p><?php echo esc_html($license_error); ?></p>
758 </div>
759 <?php endif; ?>
760 <form id="mxchat-activation-form">
761 <table class="form-table">
762 <tr valign="top">
763 <th scope="row">Email Address</th>
764 <td>
765 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text" />
766 </td>
767 </tr>
768 <tr valign="top">
769 <th scope="row">Activation Key</th>
770 <td>
771 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text" />
772 </td>
773 </tr>
774 </table>
775 <?php if ($license_status !== 'active'): ?>
776 <?php submit_button('Activate License', 'primary', 'activate_license'); ?>
777 <?php else: ?>
778 <h3>MxChat Pro</h3>
779 <?php endif; ?>
780 </form>
781 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
782 </div>
783 <?php
784 }
785
786
787
788 public function mxchat_page_init() {
789 // Register settings
790 register_setting(
791 'mxchat_option_group',
792 'mxchat_options',
793 array($this, 'mxchat_sanitize')
794 );
795
796 // Chatbot Settings Section
797 add_settings_section(
798 'mxchat_chatbot_section',
799 'Chatbot Settings',
800 null,
801 'mxchat-chatbot'
802 );
803
804 // Registering fields for the Chatbot Settings section
805 add_settings_field(
806 'api_key',
807 'API Key',
808 array($this, 'api_key_callback'),
809 'mxchat-chatbot',
810 'mxchat_chatbot_section'
811 );
812
813 add_settings_field(
814 'system_prompt_instructions',
815 'AI Instructions',
816 array($this, 'system_prompt_instructions_callback'),
817 'mxchat-chatbot',
818 'mxchat_chatbot_section'
819 );
820
821 add_settings_field(
822 'model',
823 'Model',
824 array($this, 'mxchat_model_callback'),
825 'mxchat-chatbot',
826 'mxchat_chatbot_section'
827 );
828
829 add_settings_field(
830 'top_bar_title',
831 'Top Bar Title',
832 array($this, 'mxchat_top_bar_title_callback'),
833 'mxchat-chatbot',
834 'mxchat_chatbot_section'
835 );
836
837 add_settings_field(
838 'intro_message',
839 'Introductory Message',
840 array($this, 'mxchat_intro_message_callback'),
841 'mxchat-chatbot',
842 'mxchat_chatbot_section'
843 );
844
845 add_settings_field(
846 'input_copy',
847 'Input Copy',
848 array($this, 'mxchat_input_copy_callback'),
849 'mxchat-chatbot',
850 'mxchat_chatbot_section'
851 );
852
853 add_settings_field(
854 'rate_limit',
855 'Rate Limit',
856 array($this, 'mxchat_rate_limit_callback'),
857 'mxchat-chatbot',
858 'mxchat_chatbot_section'
859 );
860
861 add_settings_field(
862 'rate_limit_message',
863 'Rate Limit Message',
864 array($this, 'mxchat_rate_limit_message_callback'),
865 'mxchat-chatbot',
866 'mxchat_chatbot_section'
867 );
868
869 add_settings_field(
870 'pre_chat_message',
871 'Pre-Chat Message',
872 array($this, 'mxchat_pre_chat_message_callback'),
873 'mxchat-chatbot',
874 'mxchat_chatbot_section'
875 );
876
877 add_settings_field(
878 'append_to_body',
879 'Append Chat Widget to Body',
880 array($this, 'mxchat_append_to_body_callback'),
881 'mxchat-chatbot',
882 'mxchat_chatbot_section'
883 );
884
885 add_settings_field(
886 'privacy_toggle',
887 'Toggle Privacy Notice',
888 array($this, 'mxchat_privacy_toggle_callback'),
889 'mxchat-chatbot',
890 'mxchat_chatbot_section'
891 );
892
893 add_settings_field(
894 'complianz_toggle',
895 'Enable Complianz',
896 array($this, 'mxchat_complianz_toggle_callback'),
897 'mxchat-chatbot',
898 'mxchat_chatbot_section'
899 );
900
901 add_settings_field(
902 'link_target_toggle',
903 'Open Links in a New Tab',
904 array($this, 'mxchat_link_target_toggle_callback'),
905 'mxchat-chatbot',
906 'mxchat_chatbot_section'
907 );
908
909 add_settings_field(
910 'chat_persistence_toggle',
911 'Enable Chat Persistence',
912 array($this, 'mxchat_chat_persistence_toggle_callback'),
913 'mxchat-chatbot',
914 'mxchat_chatbot_section'
915 );
916
917
918 // WooCommerce Settings Section
919 add_settings_section(
920 'mxchat_woocommerce_section',
921 'WooCommerce Settings',
922 null,
923 'mxchat-embed'
924 );
925
926 // Loops Settings Section
927 add_settings_section(
928 'mxchat_loops_section',
929 'Loops Settings',
930 null,
931 'mxchat-embed'
932 );
933
934 // WooCommerce Settings Fields
935 add_settings_field(
936 'enable_woocommerce_integration',
937 'Automatically Embed Products',
938 array($this, 'mxchat_enable_woocommerce_integration_callback'),
939 'mxchat-embed',
940 'mxchat_woocommerce_section'
941 );
942
943 add_settings_field(
944 'enable_woocommerce_order_access',
945 'Order History Access',
946 array($this, 'mxchat_enable_woocommerce_order_access_callback'),
947 'mxchat-embed',
948 'mxchat_woocommerce_section'
949 );
950
951 add_settings_field(
952 'woocommerce_consumer_key',
953 'WooCommerce Consumer Key',
954 array($this, 'mxchat_woocommerce_consumer_key_callback'),
955 'mxchat-embed',
956 'mxchat_woocommerce_section'
957 );
958
959 add_settings_field(
960 'woocommerce_consumer_secret',
961 'WooCommerce Consumer Secret',
962 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
963 'mxchat-embed',
964 'mxchat_woocommerce_section'
965 );
966
967 // Loops Settings Fields
968 add_settings_field(
969 'loops_api_key',
970 'Loops API Key',
971 array($this, 'mxchat_loops_api_key_callback'),
972 'mxchat-embed',
973 'mxchat_loops_section'
974 );
975
976 add_settings_field(
977 'loops_mailing_list',
978 'Loops Mailing List',
979 array($this, 'mxchat_loops_mailing_list_callback'),
980 'mxchat-embed',
981 'mxchat_loops_section'
982 );
983
984 add_settings_field(
985 'trigger_keywords',
986 'Email Capture Trigger Keywords',
987 array($this, 'mxchat_trigger_keywords_callback'),
988 'mxchat-embed',
989 'mxchat_loops_section'
990 );
991
992 add_settings_field(
993 'triggered_phrase_response',
994 'Triggered Phrase Response',
995 array($this, 'mxchat_triggered_phrase_response_callback'),
996 'mxchat-embed',
997 'mxchat_loops_section'
998 );
999
1000 add_settings_field(
1001 'email_capture_response',
1002 'Email Capture Response',
1003 array($this, 'mxchat_email_capture_response_callback'),
1004 'mxchat-embed',
1005 'mxchat_loops_section'
1006 );
1007
1008 // Theme Settings Section
1009 add_settings_section(
1010 'mxchat_theme_section',
1011 'Theme Settings',
1012 null,
1013 'mxchat-theme'
1014 );
1015
1016 add_settings_field(
1017 'close_button_color',
1018 'Close Button & Title Color',
1019 array($this, 'mxchat_close_button_color_callback'),
1020 'mxchat-theme',
1021 'mxchat_theme_section'
1022 );
1023
1024 add_settings_field(
1025 'chatbot_bg_color',
1026 'Chatbot Background Color',
1027 array($this, 'mxchat_chatbot_bg_color_callback'),
1028 'mxchat-theme',
1029 'mxchat_theme_section'
1030 );
1031
1032 add_settings_field(
1033 'user_message_bg_color',
1034 'User Message Background Color',
1035 array($this, 'mxchat_user_message_bg_color_callback'),
1036 'mxchat-theme',
1037 'mxchat_theme_section'
1038 );
1039
1040 add_settings_field(
1041 'user_message_font_color',
1042 'User Message Font Color',
1043 array($this, 'mxchat_user_message_font_color_callback'),
1044 'mxchat-theme',
1045 'mxchat_theme_section'
1046 );
1047
1048 add_settings_field(
1049 'bot_message_bg_color',
1050 'Bot Message Background Color',
1051 array($this, 'mxchat_bot_message_bg_color_callback'),
1052 'mxchat-theme',
1053 'mxchat_theme_section'
1054 );
1055
1056 add_settings_field(
1057 'bot_message_font_color',
1058 'Bot Message Font Color',
1059 array($this, 'mxchat_bot_message_font_color_callback'),
1060 'mxchat-theme',
1061 'mxchat_theme_section'
1062 );
1063
1064 add_settings_field(
1065 'top_bar_bg_color',
1066 'Top Bar Background Color',
1067 array($this, 'mxchat_top_bar_bg_color_callback'),
1068 'mxchat-theme',
1069 'mxchat_theme_section'
1070 );
1071
1072 add_settings_field(
1073 'send_button_font_color',
1074 'Send Button Color',
1075 array($this, 'mxchat_send_button_font_color_callback'),
1076 'mxchat-theme',
1077 'mxchat_theme_section'
1078 );
1079
1080 add_settings_field(
1081 'chat_input_font_color',
1082 'Chat Input Font Color',
1083 array($this, 'mxchat_chat_input_font_color_callback'),
1084 'mxchat-theme',
1085 'mxchat_theme_section'
1086 );
1087
1088 add_settings_field(
1089 'chatbot_background_color',
1090 'Floating Widget Background Color',
1091 array($this, 'mxchat_chatbot_background_color_callback'),
1092 'mxchat-theme',
1093 'mxchat_theme_section'
1094 );
1095
1096 add_settings_field(
1097 'icon_color',
1098 'Chatbot Icon Color',
1099 array($this, 'mxchat_icon_color_callback'),
1100 'mxchat-theme',
1101 'mxchat_theme_section'
1102 );
1103
1104 // General Settings Section
1105 add_settings_section(
1106 'mxchat_general_section',
1107 'Frequently Asked Questions (FAQ)',
1108 null,
1109 'mxchat-general'
1110 );
1111
1112
1113 }
1114
1115
1116
1117
1118 public function mxchat_handle_activate_license() {
1119 check_ajax_referer('mxchat_activate_license_nonce', 'security');
1120
1121 $license_key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
1122 $customer_email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
1123
1124 if (empty($license_key) || empty($customer_email)) {
1125 wp_send_json_error('Email or License Key is missing');
1126 }
1127
1128 $product_id = 'MxChatPRO';
1129
1130 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
1131
1132 if (is_wp_error($response)) {
1133 wp_send_json_error('Activation failed due to a server error');
1134 }
1135
1136 $body = wp_remote_retrieve_body($response);
1137 $data = json_decode($body);
1138
1139 if ($data && isset($data->activated) && $data->activated) {
1140 update_option('mxchat_license_status', 'active');
1141 wp_send_json_success();
1142 } else {
1143 $error_message = isset($data->error) ? $data->error : 'Activation failed';
1144 update_option('mxchat_license_status', 'inactive');
1145 update_option('mxchat_license_error', $error_message);
1146 wp_send_json_error($error_message);
1147 }
1148 }
1149
1150
1151 public function mxchat_rate_limit_callback() {
1152 $rate_limits = array('5', '10', '15', '20', '100');
1153 $selected_rate_limit = isset($this->options['rate_limit']) ? $this->options['rate_limit'] : '100';
1154
1155 // Remove the is_activated check and make it always enabled
1156 echo '<div class="pro-feature-wrapper active">';
1157 echo '<select id="rate_limit" name="mxchat_options[rate_limit]">';
1158 foreach ($rate_limits as $limit) {
1159 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
1160 }
1161 echo '</select>';
1162 echo '</div>';
1163 }
1164
1165
1166 public function mxchat_rate_limit_message_callback() {
1167 // Remove the is_activated check and make it always enabled
1168 echo '<div class="pro-feature-wrapper active">';
1169 printf(
1170 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
1171 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
1172 );
1173 echo '</div>';
1174 }
1175
1176
1177 public function mxchat_enable_woocommerce_integration_callback() {
1178 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
1179 $disabled = $this->is_activated ? '' : 'disabled';
1180 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1181
1182 echo '<div class="' . esc_attr($class) . '">';
1183 echo '<label class="toggle-switch">';
1184 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
1185 echo '<span class="slider"></span>';
1186 echo '</label>';
1187
1188 if (!$this->is_activated) {
1189 echo '<div class="pro-feature-overlay">';
1190 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1191 echo '</div>';
1192 }
1193
1194 echo '</div>';
1195 }
1196
1197
1198 public function mxchat_enable_woocommerce_order_access_callback() {
1199 $checked = isset($this->options['enable_woocommerce_order_access']) && $this->options['enable_woocommerce_order_access'] === '1' ? 'checked' : '';
1200 $disabled = $this->is_activated ? '' : 'disabled';
1201 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1202
1203 echo '<div class="' . esc_attr($class) . '">';
1204 echo '<label class="toggle-switch">';
1205 echo '<input type="checkbox" id="enable_woocommerce_order_access" name="mxchat_options[enable_woocommerce_order_access]" value="1" ' . $checked . ' ' . $disabled . '>';
1206 echo '<span class="slider"></span>';
1207 echo '</label>';
1208
1209 if (!$this->is_activated) {
1210 echo '<div class="pro-feature-overlay">';
1211 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1212 echo '</div>';
1213 }
1214
1215 echo '</div>';
1216 }
1217
1218
1219 private function mxchat_add_option_field($id, $title, $callback = '') {
1220 add_settings_field(
1221 $id,
1222 $title,
1223 $callback ? array($this, $callback) : array($this, $id . '_callback'),
1224 'mxchat-max',
1225 'mxchat_setting_section_id',
1226 $id === 'model' ? ['label_for' => 'model'] : []
1227 );
1228 }
1229
1230
1231 public function api_key_callback() {
1232 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
1233 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
1234 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
1235 }
1236
1237
1238
1239 public function mxchat_woocommerce_consumer_key_callback() {
1240 $disabled = $this->is_activated ? '' : 'disabled';
1241 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1242
1243 echo '<div class="' . esc_attr($class) . '">';
1244 printf(
1245 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
1246 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
1247 $disabled
1248 );
1249
1250 if (!$this->is_activated) {
1251 echo '<div class="pro-feature-overlay">';
1252 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1253 echo '</div>';
1254 }
1255
1256 echo '</div>';
1257 }
1258
1259 public function mxchat_woocommerce_consumer_secret_callback() {
1260 $disabled = $this->is_activated ? '' : 'disabled';
1261 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1262
1263 echo '<div class="' . esc_attr($class) . '">';
1264 printf(
1265 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
1266 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
1267 $disabled
1268 );
1269 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
1270
1271 if (!$this->is_activated) {
1272 echo '<div class="pro-feature-overlay">';
1273 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1274 echo '</div>';
1275 }
1276
1277 echo '</div>';
1278 }
1279
1280
1281
1282 public function mxchat_loops_api_key_callback() {
1283 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
1284
1285 echo '<div class="api-key-wrapper">';
1286 printf(
1287 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
1288 $loops_api_key
1289 );
1290 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
1291 echo '</div>';
1292 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
1293 }
1294
1295
1296 public function mxchat_loops_mailing_list_callback() {
1297 // Retrieve Loops API key and lists
1298 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
1299 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
1300
1301 if ($loops_api_key) {
1302 // Fetch lists from Loops API
1303 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
1304
1305 if ($lists) {
1306 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
1307 foreach ($lists as $list) {
1308 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
1309 }
1310 echo '</select>';
1311 } else {
1312 echo '<p class="description">No lists found. Please verify your API Key.</p>';
1313 }
1314 } else {
1315 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
1316 }
1317 }
1318
1319 public function mxchat_trigger_keywords_callback() {
1320 $trigger_keywords = isset($this->options['trigger_keywords']) ? $this->options['trigger_keywords'] : '';
1321 echo '<input type="text" id="trigger_keywords" name="mxchat_options[trigger_keywords]" value="' . esc_attr($trigger_keywords) . '">';
1322 echo '<p class="description">Enter comma-separated trigger keywords (e.g., subscribe, discount, newsletter).</p>';
1323 }
1324
1325 public function mxchat_triggered_phrase_response_callback() {
1326 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
1327 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
1328 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
1329 }
1330
1331
1332 public function mxchat_email_capture_response_callback() {
1333 $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.';
1334 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
1335 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
1336 }
1337
1338
1339 public function mxchat_pre_chat_message_callback() {
1340 printf(
1341 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
1342 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
1343 );
1344 }
1345
1346 // Callback for AI Instructions textarea
1347 public function system_prompt_instructions_callback() {
1348 printf(
1349 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
1350 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
1351 );
1352 }
1353
1354 public function mxchat_model_callback() {
1355 $models = array(
1356 'gpt-4o' => 'gpt-4o',
1357 'gpt-4o-mini' => 'gpt-4o-mini',
1358 'gpt-4-turbo' => 'gpt-4-turbo',
1359 'gpt-4' => 'gpt-4',
1360 'gpt-3.5-turbo' => 'gpt-3.5-turbo',
1361 );
1362
1363 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
1364
1365 echo '<select id="model" name="mxchat_options[model]">';
1366 foreach ($models as $model_value => $model_label) {
1367 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
1368 }
1369 echo '</select>';
1370 }
1371
1372 public function mxchat_top_bar_title_callback() {
1373 printf(
1374 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
1375 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
1376 );
1377 }
1378
1379 public function mxchat_intro_message_callback() {
1380 printf(
1381 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
1382 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
1383 );
1384 }
1385
1386 public function mxchat_input_copy_callback() {
1387 printf(
1388 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
1389 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
1390 );
1391 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
1392 }
1393
1394
1395
1396
1397
1398
1399 public function mxchat_close_button_color_callback() {
1400 $disabled = $this->is_activated ? '' : 'disabled';
1401
1402 echo '<div class="pro-feature-wrapper">';
1403 printf(
1404 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
1405 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
1406 esc_attr($disabled)
1407 );
1408
1409 if (!$this->is_activated) {
1410 echo '<div class="pro-feature-overlay">';
1411 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1412 echo '</div>';
1413 }
1414
1415 echo '</div>';
1416 }
1417
1418 public function mxchat_chatbot_bg_color_callback() {
1419 $disabled = $this->is_activated ? '' : 'disabled';
1420
1421 echo '<div class="pro-feature-wrapper">';
1422 printf(
1423 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
1424 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
1425 esc_attr($disabled)
1426 );
1427
1428 if (!$this->is_activated) {
1429 echo '<div class="pro-feature-overlay">';
1430 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1431 echo '</div>';
1432 }
1433
1434 echo '</div>';
1435 }
1436
1437 public function mxchat_user_message_bg_color_callback() {
1438 $disabled = $this->is_activated ? '' : 'disabled';
1439
1440 echo '<div class="pro-feature-wrapper">';
1441 printf(
1442 '<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 />',
1443 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
1444 esc_attr($disabled)
1445 );
1446
1447 if (!$this->is_activated) {
1448 echo '<div class="pro-feature-overlay">';
1449 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1450 echo '</div>';
1451 }
1452
1453 echo '</div>';
1454 }
1455
1456 public function mxchat_user_message_font_color_callback() {
1457 $disabled = $this->is_activated ? '' : 'disabled';
1458
1459 echo '<div class="pro-feature-wrapper">';
1460 printf(
1461 '<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 />',
1462 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
1463 esc_attr($disabled)
1464 );
1465
1466 if (!$this->is_activated) {
1467 echo '<div class="pro-feature-overlay">';
1468 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1469 echo '</div>';
1470 }
1471
1472 echo '</div>';
1473 }
1474
1475 public function mxchat_bot_message_bg_color_callback() {
1476 $disabled = $this->is_activated ? '' : 'disabled';
1477
1478 echo '<div class="pro-feature-wrapper">';
1479 printf(
1480 '<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 />',
1481 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
1482 esc_attr($disabled)
1483 );
1484
1485 if (!$this->is_activated) {
1486 echo '<div class="pro-feature-overlay">';
1487 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1488 echo '</div>';
1489 }
1490
1491 echo '</div>';
1492 }
1493
1494 public function mxchat_bot_message_font_color_callback() {
1495 $disabled = $this->is_activated ? '' : 'disabled';
1496
1497 echo '<div class="pro-feature-wrapper">';
1498 printf(
1499 '<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 />',
1500 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
1501 esc_attr($disabled)
1502 );
1503
1504 if (!$this->is_activated) {
1505 echo '<div class="pro-feature-overlay">';
1506 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1507 echo '</div>';
1508 }
1509
1510 echo '</div>';
1511 }
1512
1513 public function mxchat_top_bar_bg_color_callback() {
1514 $disabled = $this->is_activated ? '' : 'disabled';
1515
1516 echo '<div class="pro-feature-wrapper">';
1517 printf(
1518 '<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 />',
1519 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
1520 esc_attr($disabled)
1521 );
1522
1523 if (!$this->is_activated) {
1524 echo '<div class="pro-feature-overlay">';
1525 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1526 echo '</div>';
1527 }
1528
1529 echo '</div>';
1530 }
1531
1532 public function mxchat_send_button_font_color_callback() {
1533 $disabled = $this->is_activated ? '' : 'disabled';
1534
1535 echo '<div class="pro-feature-wrapper">';
1536 printf(
1537 '<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 />',
1538 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
1539 esc_attr($disabled)
1540 );
1541
1542 if (!$this->is_activated) {
1543 echo '<div class="pro-feature-overlay">';
1544 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1545 echo '</div>';
1546 }
1547
1548 echo '</div>';
1549 }
1550
1551 public function mxchat_chatbot_background_color_callback() {
1552 $disabled = $this->is_activated ? '' : 'disabled';
1553
1554 echo '<div class="pro-feature-wrapper">';
1555 printf(
1556 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
1557 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
1558 esc_attr($disabled)
1559 );
1560
1561 if (!$this->is_activated) {
1562 echo '<div class="pro-feature-overlay">';
1563 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1564 echo '</div>';
1565 }
1566
1567 echo '</div>';
1568 }
1569
1570 public function mxchat_icon_color_callback() {
1571 $disabled = $this->is_activated ? '' : 'disabled';
1572
1573 echo '<div class="pro-feature-wrapper">';
1574 printf(
1575 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
1576 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
1577 esc_attr($disabled)
1578 );
1579
1580 if (!$this->is_activated) {
1581 echo '<div class="pro-feature-overlay">';
1582 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1583 echo '</div>';
1584 }
1585
1586 echo '</div>';
1587 }
1588
1589 public function mxchat_chat_input_font_color_callback() {
1590 $disabled = $this->is_activated ? '' : 'disabled';
1591
1592 echo '<div class="pro-feature-wrapper">';
1593 printf(
1594 '<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 />',
1595 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
1596 esc_attr($disabled)
1597 );
1598
1599 if (!$this->is_activated) {
1600 echo '<div class="pro-feature-overlay">';
1601 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1602 echo '</div>';
1603 }
1604
1605 echo '</div>';
1606 }
1607
1608
1609 public function mxchat_append_to_body_callback() {
1610 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
1611 echo '<label class="toggle-switch">';
1612 printf(
1613 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
1614 esc_attr($append_to_body_checked)
1615 );
1616 echo '<span class="slider"></span>';
1617 echo '</label>';
1618 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>';
1619 }
1620
1621
1622 public function mxchat_privacy_toggle_callback() {
1623 // Check if the privacy toggle is enabled
1624 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
1625
1626 // Retrieve the stored privacy text if it exists
1627 $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>.';
1628
1629 // Output the toggle switch
1630 echo '<label class="toggle-switch">';
1631 printf(
1632 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
1633 esc_attr($privacy_toggle_checked)
1634 );
1635 echo '<span class="slider"></span>';
1636 echo '</label>';
1637 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
1638
1639 // Output the custom text input field
1640 printf(
1641 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
1642 esc_textarea($privacy_text)
1643 );
1644 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
1645 }
1646
1647
1648 public function mxchat_complianz_toggle_callback() {
1649 // Check if the Complianz toggle is enabled
1650 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
1651
1652 // Check if the plugin is activated (paid feature)
1653 $disabled = $this->is_activated ? '' : 'disabled';
1654 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1655
1656 echo '<div class="' . esc_attr($class) . '">';
1657
1658 // Output the toggle switch
1659 echo '<label class="toggle-switch">';
1660 printf(
1661 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
1662 esc_attr($complianz_toggle_checked),
1663 esc_attr($disabled)
1664 );
1665 echo '<span class="slider"></span>';
1666 echo '</label>';
1667 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot.</p>';
1668
1669 // If the feature is not activated, show the Pro feature overlay
1670 if (!$this->is_activated) {
1671 echo '<div class="pro-feature-overlay">';
1672 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1673 echo '</div>';
1674 }
1675
1676 echo '</div>';
1677 }
1678
1679
1680
1681
1682
1683 public function mxchat_link_target_toggle_callback() {
1684 // Check if the toggle is enabled in the options
1685 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
1686
1687 // Output the toggle switch
1688 echo '<label class="toggle-switch">';
1689 printf(
1690 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
1691 esc_attr($link_target_toggle)
1692 );
1693 echo '<span class="slider"></span>';
1694 echo '</label>';
1695 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
1696 }
1697
1698 public function mxchat_chat_persistence_toggle_callback() {
1699 // Check if chat persistence toggle is enabled
1700 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
1701
1702 // Check if the plugin is activated (paid feature)
1703 $disabled = $this->is_activated ? '' : 'disabled';
1704 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
1705
1706 echo '<div class="' . esc_attr($class) . '">';
1707
1708 // Output the toggle switch
1709 echo '<label class="toggle-switch">';
1710 printf(
1711 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
1712 esc_attr($chat_persistence_toggle_checked),
1713 esc_attr($disabled)
1714 );
1715 echo '<span class="slider"></span>';
1716 echo '</label>';
1717 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
1718
1719 // If not activated, show Pro feature overlay
1720 if (!$this->is_activated) {
1721 echo '<div class="pro-feature-overlay">';
1722 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
1723 echo '</div>';
1724 }
1725
1726 echo '</div>';
1727 }
1728
1729
1730
1731
1732
1733 public function mxchat_enqueue_admin_assets() {
1734 wp_enqueue_style('wp-color-picker');
1735
1736 // Get the plugin version or file modification time for cache busting
1737 $plugin_version = '1.1.7'; // Replace this with your plugin's version
1738
1739 // File paths
1740 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
1741 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
1742 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
1743 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
1744
1745 // Check if files exist and get modification times
1746 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
1747 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
1748 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
1749 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
1750
1751 // Enqueue scripts and styles with corrected paths
1752 wp_enqueue_script(
1753 'mxchat-color-picker',
1754 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
1755 array('wp-color-picker'),
1756 $color_picker_version,
1757 true
1758 );
1759
1760 wp_enqueue_script(
1761 'mxchat-embedding-check',
1762 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
1763 array(),
1764 $embedding_check_version,
1765 true
1766 );
1767
1768 wp_enqueue_script(
1769 'mxchat-transcripts-js',
1770 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
1771 array('jquery'),
1772 $transcripts_js_version,
1773 true
1774 );
1775
1776 wp_enqueue_script(
1777 'mxchat-admin-js',
1778 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
1779 array('jquery'),
1780 $plugin_version,
1781 true
1782 );
1783
1784 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
1785 'ajax_url' => admin_url('admin-ajax.php'),
1786 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
1787 ));
1788
1789 wp_enqueue_style(
1790 'mxchat-admin-css',
1791 plugin_dir_url(__FILE__) . '../css/admin-style.css',
1792 array(),
1793 $admin_css_version
1794 );
1795
1796 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
1797 'ajax_url' => admin_url('admin-ajax.php'),
1798 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
1799 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
1800 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
1801 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
1802 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
1803 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
1804 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
1805 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
1806 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
1807 'icon_color' => $this->options['icon_color'] ?? '#fff',
1808 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
1809 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
1810 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
1811
1812 // New fields for Loops Integration
1813 'loops_api_key' => $this->options['loops_api_key'] ?? '',
1814 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
1815 'trigger_keywords' => $this->options['trigger_keywords'] ?? '',
1816 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
1817 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.'
1818 ));
1819
1820
1821 }
1822
1823 public function mxchat_sanitize($input) {
1824 $new_input = array();
1825
1826 if (isset($input['api_key'])) {
1827 $new_input['api_key'] = sanitize_text_field($input['api_key']);
1828 }
1829
1830 if (isset($input['enable_woocommerce_integration'])) {
1831 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
1832
1833 }
1834
1835 if (isset($input['privacy_toggle'])) {
1836 $new_input['privacy_toggle'] = $input['privacy_toggle'];
1837 }
1838
1839 if (isset($input['complianz_toggle'])) {
1840 $new_input['complianz_toggle'] = $input['complianz_toggle'];
1841 }
1842
1843 // Handle custom privacy text input
1844 if (isset($input['privacy_text'])) {
1845 // Allow basic HTML for links
1846 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
1847 }
1848
1849
1850 if (isset($input['enable_woocommerce_order_access'])) {
1851 $new_input['enable_woocommerce_order_access'] = isset($input['enable_woocommerce_order_access']) && $input['enable_woocommerce_order_access'] === '1' ? '1' : '0';
1852
1853 }
1854
1855 if (isset($input['system_prompt_instructions'])) {
1856 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
1857 }
1858
1859 if (isset($input['mxchat_pro_email'])) {
1860 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
1861 }
1862
1863 if (isset($input['mxchat_activation_key'])) {
1864 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
1865 }
1866
1867 if (isset($input['append_to_body'])) {
1868 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
1869 }
1870
1871 if (isset($input['top_bar_title'])) {
1872 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
1873 }
1874
1875 if (isset($input['intro_message'])) {
1876 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
1877 }
1878
1879 if (isset($input['input_copy'])) {
1880 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
1881 }
1882
1883
1884 if (isset($input['rate_limit_message'])) {
1885 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
1886 }
1887
1888 if (isset($input['rate_limit'])) {
1889 $allowed_limits = array('5', '10', '15', '20', '100');
1890 $rate_limit = sanitize_text_field($input['rate_limit']);
1891
1892 if (in_array($rate_limit, $allowed_limits, true)) {
1893 $new_input['rate_limit'] = $rate_limit;
1894 } else {
1895 // Set a default or a fallback value in case of an invalid entry
1896 $new_input['rate_limit'] = '100';
1897 }
1898 }
1899
1900
1901 if (isset($input['pre_chat_message'])) {
1902 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
1903 }
1904
1905 if (isset($input['model'])) {
1906 $allowed_models = array(
1907 'gpt-4o',
1908 'gpt-4o-mini',
1909 'gpt-4-turbo',
1910 'gpt-4',
1911 'gpt-3.5-turbo',
1912 );
1913 if (in_array($input['model'], $allowed_models)) {
1914 $new_input['model'] = sanitize_text_field($input['model']);
1915 }
1916 }
1917
1918 // Sanitize new pro features
1919 if (isset($input['close_button_color'])) {
1920 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
1921 }
1922
1923 if (isset($input['chatbot_bg_color'])) {
1924 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
1925 }
1926
1927 if (isset($input['woocommerce_consumer_key'])) {
1928 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
1929 }
1930
1931 if (isset($input['woocommerce_consumer_secret'])) {
1932 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
1933 }
1934
1935 if (isset($input['user_message_bg_color'])) {
1936 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
1937 }
1938
1939 if (isset($input['user_message_font_color'])) {
1940 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
1941 }
1942
1943 if (isset($input['bot_message_bg_color'])) {
1944 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
1945 }
1946
1947 if (isset($input['bot_message_font_color'])) {
1948 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
1949 }
1950
1951 if (isset($input['top_bar_bg_color'])) {
1952 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
1953 }
1954
1955 if (isset($input['send_button_font_color'])) {
1956 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
1957 }
1958
1959 if (isset($input['chatbot_background_color'])) {
1960 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
1961 }
1962
1963 if (isset($input['icon_color'])) {
1964 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
1965 }
1966
1967 if (isset($input['chat_input_font_color'])) {
1968 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
1969 }
1970
1971 // Sanitize link_target_toggle
1972 if (isset($input['link_target_toggle'])) {
1973 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
1974 }
1975
1976 // Sanitize Loops API Key
1977 if (isset($input['loops_api_key'])) {
1978 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
1979 }
1980
1981 if (isset($input['chat_persistence_toggle'])) {
1982 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
1983 }
1984
1985
1986 // Sanitize Loops Mailing List
1987 if (isset($input['loops_mailing_list'])) {
1988 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
1989 }
1990
1991 // Sanitize Trigger Keywords
1992 if (isset($input['trigger_keywords'])) {
1993 $new_input['trigger_keywords'] = sanitize_text_field($input['trigger_keywords']);
1994 }
1995
1996 // Sanitize Triggered Phrase Response
1997 if (isset($input['triggered_phrase_response'])) {
1998 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
1999 }
2000
2001 if (isset($input['email_capture_response'])) {
2002 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
2003 }
2004
2005 return $new_input;
2006 }
2007
2008
2009 // Method to append the chatbot to the body
2010 public function mxchat_append_chatbot_to_body() {
2011 $options = get_option('mxchat_options');
2012 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
2013 echo do_shortcode('[mxchat_chatbot floating="yes"]');
2014 }
2015 }
2016
2017
2018
2019 private function mxchat_extract_main_content($html) {
2020 $dom = new DOMDocument;
2021 libxml_use_internal_errors(true); // Suppress HTML parsing errors
2022 @$dom->loadHTML($html);
2023 libxml_clear_errors();
2024
2025 $xpath = new DOMXPath($dom);
2026
2027 // Simplified selectors focusing on common content areas
2028 $selectors = [
2029 '//article',
2030 '//*[@id="content"]',
2031 '//*[@class="entry-content"]',
2032 '//main',
2033 ];
2034
2035 foreach ($selectors as $selector) {
2036 $nodes = $xpath->query($selector);
2037 if ($nodes->length > 0) {
2038 $content = '';
2039 foreach ($nodes as $node) {
2040 $content .= $dom->saveHTML($node);
2041 }
2042 return $content;
2043 }
2044 }
2045
2046 // Fallback: Return the entire body content if no specific selector matches
2047 $body = $dom->getElementsByTagName('body');
2048 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
2049 }
2050
2051 public function mxchat_handle_sitemap_submission() {
2052 // Check if the form was submitted and the user has sufficient permissions
2053 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
2054 return;
2055 }
2056
2057 // Verify the nonce field for security
2058 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
2059
2060 // Sanitize and retrieve the submitted URL
2061 $submitted_url = esc_url_raw($_POST['sitemap_url']); // Accept either a sitemap URL or a regular URL
2062
2063 // Fetch the content of the submitted URL
2064 $response = wp_remote_get($submitted_url);
2065 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
2066 set_transient('mxchat_admin_notice_error', 'Failed to fetch the URL. Please check the URL and try again.', 30);
2067 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2068 exit;
2069 }
2070
2071 $content_type = wp_remote_retrieve_header($response, 'content-type');
2072 $body_content = wp_remote_retrieve_body($response);
2073
2074 // Check if the URL points to a sitemap (XML) or a regular HTML page
2075 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
2076 // Handle Sitemap XML
2077 $xml = simplexml_load_string($body_content);
2078 if ($xml === false) {
2079 set_transient('mxchat_admin_notice_error', 'Invalid sitemap XML. Please provide a valid sitemap.', 30);
2080 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2081 exit;
2082 }
2083
2084 $embedding_success = true; // Flag to track if all embeddings are successful
2085
2086 foreach ($xml->url as $url_element) {
2087 $page_url = (string)$url_element->loc;
2088
2089 $page_response = wp_remote_get($page_url);
2090 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
2091 continue;
2092 }
2093
2094 $page_html = wp_remote_retrieve_body($page_response);
2095 $page_content = $this->mxchat_extract_main_content($page_html);
2096 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2097
2098 if (!empty($sanitized_content)) {
2099 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2100 if (is_array($embedding_vector)) {
2101 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $this->options['api_key']);
2102 } else {
2103 $embedding_success = false; // Set flag to false if any embedding fails
2104 }
2105 }
2106 }
2107
2108 if ($embedding_success) {
2109 set_transient('mxchat_admin_notice_success', 'Sitemap content successfully submitted!', 30);
2110 } else {
2111 set_transient('mxchat_admin_notice_error', 'Some content failed to embed. Please check your API key and try again.', 30);
2112 }
2113
2114 } else {
2115 // Handle Regular URL (HTML Page)
2116 $page_content = $this->mxchat_extract_main_content($body_content);
2117 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2118
2119 if (!empty($sanitized_content)) {
2120 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2121 if (is_array($embedding_vector)) {
2122 MxChat_Utils::submit_content_to_db($sanitized_content, $submitted_url, $this->options['api_key']);
2123 set_transient('mxchat_admin_notice_success', 'URL content successfully submitted!', 30);
2124 } else {
2125 set_transient('mxchat_admin_notice_error', 'Failed to generate embedding for the URL content. Please check your API key and try again.', 30);
2126 }
2127 } else {
2128 set_transient('mxchat_admin_notice_error', 'No valid content found on the provided URL.', 30);
2129 }
2130 }
2131
2132 // Redirect after setting the transient
2133 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2134 exit;
2135 }
2136
2137
2138
2139 private function mxchat_sanitize_content_for_api($content) {
2140 // Remove script, style tags, and HTML comments
2141 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2142 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2143 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2144
2145 // Remove all HTML tags and decode HTML entities
2146 $content = wp_strip_all_tags($content);
2147 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2148
2149 // Trim and normalize whitespace
2150 $content = trim(preg_replace('/\s+/', ' ', $content));
2151
2152 return $content;
2153 }
2154
2155
2156
2157 private function mxchat_fetch_loops_mailing_lists($api_key) {
2158 $url = 'https://app.loops.so/api/v1/lists';
2159 $response = wp_remote_get($url, array(
2160 'headers' => array(
2161 'Authorization' => 'Bearer ' . $api_key,
2162 'Content-Type' => 'application/json'
2163 )
2164 ));
2165
2166 if (is_wp_error($response)) {
2167 return array();
2168 }
2169
2170 $body = wp_remote_retrieve_body($response);
2171 $lists = json_decode($body, true);
2172
2173 return isset($lists) && is_array($lists) ? $lists : array();
2174 }
2175
2176
2177
2178 }
2179 ?>
2180