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

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