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

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

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