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

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

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