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

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