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

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