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

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

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