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

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

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