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

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