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

4,331 lines 174.8 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 // Add the AJAX handler for logged-in users
37 add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
38 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39
40 // Define admin_post actions for form submission handling
41 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
42 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
43 add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
44 add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
45
46 }
47
48 // Method to check if the license is active
49 private function is_license_active() {
50 $license_status = get_option('mxchat_license_status', 'inactive');
51 return $license_status === 'active';
52 }
53
54 // Initialize default options
55 private function initialize_default_options() {
56 $default_options = array(
57 'api_key' => '',
58 'xai_api_key' => '',
59 'claude_api_key' => '',
60 '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:
61 - Your name is [Chatbot Name].
62 - 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.
63 - Do not make up links to pages. Only use specific links you see in your knowledgebase.
64 - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
65 - 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.',
66 'model' => 'gpt-4o',
67 'rate_limit_logged_in' => '100',
68 'rate_limit_logged_out' => '100',
69 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
70 'top_bar_title' => 'MxChat',
71 'intro_message' => 'Hello! How can I assist you today?',
72 'input_copy' => 'How can I assist?',
73 'append_to_body' => 'off',
74 'close_button_color' => '#fff',
75 'chatbot_bg_color' => '#fff',
76 'user_message_bg_color' => '#fff',
77 'user_message_font_color' => '#212121',
78 'bot_message_bg_color' => '#212121',
79 'bot_message_font_color' => '#fff',
80 'top_bar_bg_color' => '#212121',
81 'send_button_font_color' => '#212121',
82 'chat_input_font_color' => '#212121',
83 'chatbot_background_color' => '#212121',
84 'icon_color' => '#fff',
85 'enable_woocommerce_integration' => '0',
86 'link_target_toggle' => 'off',
87 'pre_chat_message' => 'Hey there! Ask me anything!',
88
89 // New fields for Loops Integration
90 'loops_api_key' => '',
91 'loops_mailing_list' => '',
92 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
93 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
94 'popular_question_1' => '',
95 'popular_question_2' => '',
96 'popular_question_3' => '',
97 'pdf_intent_trigger_text' => "Please provide the URL to the PDF you'd like to discuss.",
98 'pdf_intent_success_text' => "I've processed the PDF. What questions do you have about it?",
99 'pdf_intent_error_text' => "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
100 'pdf_max_pages' => 69,
101
102 // Live Agent Integration
103 'live_agent_webhook_url' => '',
104 'live_agent_secret_key' => '',
105 'live_agent_bot_token' => '',
106 'live_agent_message_bg_color' => '#ffffff',
107 'live_agent_message_font_color' => '#333333',
108 'chat_toolbar_toggle' => 'off',
109 'mode_indicator_bg_color' => '#767676',
110 'mode_indicator_font_color' => '#ffffff',
111 'toolbar_icon_color' => '#212121',
112 );
113
114
115 // Merge existing options with defaults
116 $existing_options = get_option('mxchat_options', array());
117 $merged_options = wp_parse_args($existing_options, $default_options);
118
119 // Update the options if they have changed
120 if ($existing_options !== $merged_options) {
121 update_option('mxchat_options', $merged_options);
122 }
123
124 // Update the $this->options property
125 $this->options = $merged_options;
126 }
127
128
129
130 public function mxchat_add_plugin_page() {
131 // Main menu page
132 add_menu_page(
133 'MxChat Settings',
134 'MxChat',
135 'manage_options',
136 'mxchat-max',
137 array($this, 'mxchat_create_admin_page'),
138 'dashicons-testimonial',
139 6
140 );
141
142 // Submenu page for Knowledge
143 add_submenu_page(
144 'mxchat-max',
145 'Prompts',
146 'Knowledge',
147 'manage_options',
148 'mxchat-prompts',
149 array($this, 'mxchat_create_prompts_page')
150 );
151
152 // Submenu page for Chat Transcripts
153 add_submenu_page(
154 'mxchat-max', // Corrected parent slug to match the main menu
155 'Chat Transcripts',
156 'Transcripts',
157 'manage_options',
158 'mxchat-transcripts',
159 array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
160 );
161
162 // Submenu page for Intents
163 add_submenu_page(
164 'mxchat-max', // Parent slug
165 'MxChat Intents', // Page title
166 'Intents', // Menu title
167 'manage_options', // Capability
168 'mxchat-intents', // Menu slug
169 array($this, 'mxchat_intents_page_html') // Callback function
170 );
171
172 // Submenu page for Activation Key
173 add_submenu_page(
174 'mxchat-max',
175 'Pro Upgrade',
176 'Pro Upgrade',
177 'manage_options',
178 'mxchat-activation',
179 array($this, 'mxchat_create_activation_page')
180 );
181
182
183 }
184
185
186 public function mxchat_handle_content_submission() {
187 // Check if the form was submitted and the user has sufficient permissions
188 if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
189 return;
190 }
191
192 // Verify the nonce field for security
193 $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
194 if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
195 wp_die('Nonce verification failed.');
196 }
197
198 // Sanitize the content input
199 $article_content = sanitize_textarea_field($_POST['article_content']);
200
201 // Sanitize the URL input (optional URL)
202 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
203
204 // Generate the embedding vector for the content
205 $embedding_vector = $this->mxchat_generate_embedding($article_content);
206
207 global $wpdb;
208 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
209
210 // Check if the 'source_url' column exists in the table and add it if it doesn't
211 if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
212 // Use wpdb::query and a prepared statement to avoid SQL injection
213 $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
214 }
215
216 if (is_array($embedding_vector)) {
217 // Serialize the embedding vector before storing it
218 $embedding_vector_serialized = serialize($embedding_vector);
219
220 // Insert the content, embedding vector, and source URL into the database, using a prepared statement
221 $inserted = $wpdb->insert(
222 $table_name,
223 array(
224 'article_content' => $article_content,
225 'embedding_vector' => $embedding_vector_serialized,
226 'source_url' => $article_url, // Insert the URL, empty string if not provided
227 ),
228 array(
229 '%s', // Format for article_content (string)
230 '%s', // Format for embedding_vector (serialized string)
231 '%s', // Format for source_url (string)
232 )
233 );
234
235 if ($inserted === false) {
236 //error_log('Error inserting content: ' . $wpdb->last_error);
237 set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
238 } else {
239 set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
240 }
241
242 } else {
243 //error_log('Embedding generation failed for article content: ' . $article_content);
244 set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
245 }
246
247 // Redirect after setting the transient
248 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
249 exit;
250 }
251
252 public function mxchat_display_admin_notice() {
253 // Success notice
254 if ($message = get_transient('mxchat_admin_notice_success')) {
255 ?>
256 <div class="notice notice-success is-dismissible">
257 <p><?php echo esc_html($message); ?></p>
258 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
259 </div>
260 <?php
261 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
262 }
263
264 // Error notice
265 if ($message = get_transient('mxchat_admin_notice_error')) {
266 ?>
267 <div class="notice notice-error is-dismissible">
268 <p><?php echo esc_html($message); ?></p>
269 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
270 </div>
271 <?php
272 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
273 }
274 }
275
276
277
278
279
280 public function mxchat_create_admin_page() {
281 ?>
282 <div class="wrap mxchat-admin">
283 <?php if (!$this->is_activated): ?>
284 <div class="mxchat-pro-banner">
285 <p>
286 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!
287 <a href="https://mxchat.ai/" target="_blank">Upgrade to MxChat Pro today!</a>
288 </p>
289 </div>
290 <?php endif; ?>
291 <h2 class="admin-title">Mx<span class="admin-emphasis">Chat</span></h2>
292 <h2 class="mxchat-nav-tab-wrapper">
293 <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
294 <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
295 <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
296 <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
297 </h2>
298 <form method="post" action="options.php">
299 <?php settings_fields('mxchat_option_group'); ?>
300 <div id="chatbot" class="mxchat-tab-content active">
301 <?php do_settings_sections('mxchat-chatbot'); ?>
302 </div>
303
304
305
306 <div id="embed" class="mxchat-tab-content">
307 <div class="mxchat-settings-section">
308 <h2>WooCommerce Settings</h2>
309 <table class="form-table">
310 <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
311 </table>
312 </div>
313
314 <div class="section-divider"></div> <!-- Divider -->
315
316 <div class="mxchat-settings-section">
317 <h2>Loops Settings</h2>
318 <table class="form-table">
319 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
320 </table>
321 </div>
322
323
324 <div class="section-divider"></div> <!-- Divider -->
325
326 <!-- Brave Search Settings Section -->
327 <div class="mxchat-settings-section">
328 <h2>Brave Search Settings</h2>
329 <table class="form-table">
330 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
331 </table>
332 </div>
333
334 <div class="section-divider"></div> <!-- Divider -->
335
336 <!-- Chat with PDF Intent Settings Section -->
337 <div class="mxchat-settings-section">
338 <h2>Toolbar Settings & Intents</h2>
339 <table class="form-table">
340 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
341 </table>
342 </div>
343
344 <div class="section-divider"></div> <!-- Divider -->
345 <!-- Live Agent Settings Section -->
346 <div class="mxchat-settings-section">
347 <h2>Live Agent Settings</h2>
348 <p>Visit our <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank">documentation page</a> to set up live agent transfer via Slack.</p>
349 <table class="form-table">
350 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
351 </table>
352 </div>
353
354 </div>
355
356
357
358
359
360 <div id="theme" class="mxchat-tab-content">
361 <?php do_settings_sections('mxchat-theme'); ?>
362 </div>
363 <div id="general" class="mxchat-tab-content">
364 <?php do_settings_sections('mxchat-general'); ?>
365 <p>
366 If you’re having trouble with setup or getting the responses you need, we encourage you to review our
367 <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer">documentation</a> or
368 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer">create a support ticket</a>.
369 </p>
370 <p>
371 If you like our plugin, please consider <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer">leaving us a review</a>.
372 </p>
373
374 <div class="faq-item">
375 <h3>How does the Claude API integration work?</h3>
376 <p>
377 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.
378 </p>
379 <p>
380 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.
381 </p>
382 <p>
383 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>.
384 </p>
385 </div>
386
387 <div class="faq-item">
388 <h3>How does the X.AI API integration work?</h3>
389 <p>
390 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.
391 </p>
392 <p>
393 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>.
394 </p>
395 </div>
396
397 <div class="faq-item">
398 <h3>Do I need an OpenAI API key to use the chatbot?</h3>
399 <p>
400 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.
401 </p>
402 <p>
403 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.
404 </p>
405 </div>
406
407 <div class="faq-item">
408 <h3>How do I add the chatbot to my site?</h3>
409 <p>
410 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.
411 </p>
412 </div>
413
414 <div class="faq-item">
415 <h3>How does the chatbot use my content to generate responses?</h3>
416 <p>
417 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.
418 </p>
419 </div>
420
421 <div class="faq-item">
422 <h3>Why does the chatbot sometimes make up links or information?</h3>
423 <p>
424 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.
425 </p>
426 </div>
427
428 <div class="faq-item">
429 <h3>How do intents work in MxChat?</h3>
430 <p>
431 Intents allow MxChat to recognize user requests and trigger specific actions, such as capturing emails or displaying product information. This helps provide a more interactive and responsive experience. For a detailed explanation of each intent, please refer to our <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">Intents Documentation</a>.
432 </p>
433 </div>
434
435
436 <div class="faq-item">
437 <h3>How does the Complianz integration work?</h3>
438 <p>
439 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.
440 </p>
441 </div>
442
443 <div class="faq-item">
444 <h3>How does the WooCommerce integration work?</h3>
445 <p>
446 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.
447 </p>
448 </div>
449
450 <div class="faq-item">
451 <h3>Why isn't my chatbot responding as expected?</h3>
452 <p>
453 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.
454 </p>
455 <p>
456 We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
457 </p>
458 </div>
459
460 <div class="faq-item">
461 <h3>What is Loops, and how do I get an API key?</h3>
462 <p>
463 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.
464 </p>
465 </div>
466
467
468 </div>
469 <?php submit_button(); ?>
470 </form>
471 </div>
472 <?php
473 }
474
475
476 public function mxchat_create_transcripts_page() {
477 ?>
478 <div class="wrap mxchat-admin">
479 <h2><?php esc_html_e('Chat Transcripts', 'mxchat'); ?></h2>
480 <form id="mxchat-delete-form" method="post">
481 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
482 <div class="mxchat-controls">
483 <label for="mxchat-select-all-transcripts" class="mxchat-select-all-label">
484 <input type="checkbox" id="mxchat-select-all-transcripts" /> <?php esc_html_e('Select All', 'mxchat'); ?>
485 </label>
486 <input type="submit" value="<?php esc_attr_e('Delete Selected', 'mxchat'); ?>" class="button delete-chats-button" />
487 </div>
488 <div id="mxchat-transcripts">
489 <!-- Transcripts will be loaded here -->
490 </div>
491 </form>
492 </div>
493 <?php
494 }
495
496
497
498
499 public function mxchat_create_prompts_page() {
500 global $wpdb;
501 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
502
503 // Display success message if all prompts were deleted
504 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
505 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
506 }
507
508 // Set up pagination and search query
509 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
510 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
511 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
512 $per_page = 10;
513 $offset = ($current_page - 1) * $per_page;
514
515 // Modify query to handle search input
516 $sql_search = "";
517 if ($search_query) {
518 $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
519 }
520
521 // Retrieve total number of prompts, considering search filter
522 $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
523 $total_pages = ceil($total_prompts / $per_page);
524
525 // Retrieve prompts from the database
526 $prompts = $wpdb->get_results(
527 $wpdb->prepare(
528 "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
529 $per_page,
530 $offset
531 )
532 );
533
534 ?>
535
536 <div class="wrap mxchat-admin">
537 <div class="mxchat-grid-container">
538 <!-- Submit Content Form -->
539 <div class="mxchat-grid-item full-width">
540 <h2>Submit Content</h2>
541 <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
542 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
543 <div class="mxchat-form-group">
544 <label for="article_content">Article Content:</label>
545 <textarea name="article_content" id="article_content" required></textarea>
546 </div>
547 <div class="mxchat-form-group">
548 <label for="article_url">Article URL (Optional):</label>
549 <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
550 </div>
551 <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
552 </form>
553 <div id="mxchat-content-loading" class="mxchat-content-spinner" style="display: none;"></div>
554 <div id="mxchat-content-loading-text" class="mxchat-loading-text">Submitting content, please wait...</div>
555 </div>
556
557
558 <div class="mxchat-grid-item">
559 <h2><?php esc_html_e('Submit Sitemap, URL, or PDF URL', 'mxchat'); ?></h2>
560
561 <?php
562 // Check for any active processing
563 $pdf_url = get_transient('mxchat_last_pdf_url');
564 $sitemap_url = get_transient('mxchat_last_sitemap_url');
565 $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
566 $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
567
568 // Clear old completed statuses
569 if ($pdf_status && $pdf_status['status'] === 'complete') {
570 delete_transient('mxchat_last_pdf_url');
571 $pdf_status = false;
572 }
573 if ($sitemap_status && $sitemap_status['status'] === 'complete') {
574 delete_transient('mxchat_last_sitemap_url');
575 $sitemap_status = false;
576 }
577
578 $is_processing = ($pdf_status && $pdf_status['status'] === 'processing') ||
579 ($sitemap_status && $sitemap_status['status'] === 'processing');
580 ?>
581
582 <?php if (!$is_processing) : ?>
583 <form id="mxchat-sitemap-form" method="post" class="mxchat-sitemap-form"
584 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
585
586 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
587
588 <div class="mxchat-search-group">
589 <input type="url"
590 name="sitemap_url"
591 id="sitemap_url"
592 placeholder="<?php esc_attr_e('Enter URL (Sitemap, PDF, or webpage)', 'mxchat'); ?>"
593 required
594 aria-label="<?php esc_attr_e('URL Input', 'mxchat'); ?>"
595 />
596 <input type="submit"
597 name="submit_sitemap"
598 value="<?php esc_attr_e('Submit', 'mxchat'); ?>"
599 class="button button-primary"
600 />
601 </div>
602 </form>
603 <?php endif; ?>
604
605 <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"
606 aria-hidden="true"></div>
607 <div id="mxchat-loading-text" class="screen-reader-text" style="display: none;">
608 <?php esc_html_e('Loading information into database, please wait...', 'mxchat'); ?>
609 </div>
610
611 <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
612 <div class="mxchat-process-status pdf" role="alert" aria-live="polite">
613 <h3><?php esc_html_e('PDF Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
614 <p>
615 <?php
616 printf(
617 /* translators: 1: Current pages, 2: Total pages, 3: Percentage complete */
618 esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
619 absint($pdf_status['processed_pages']),
620 absint($pdf_status['total_pages']),
621 absint($pdf_status['percentage'])
622 );
623 ?>
624 </p>
625 <p>
626 <?php
627 printf(
628 /* translators: %s: Current status */
629 esc_html__('Status: %s', 'mxchat'),
630 esc_html(ucfirst($pdf_status['status']))
631 );
632 ?>
633 </p>
634 <p>
635 <?php
636 printf(
637 /* translators: %s: Time since last update */
638 esc_html__('Last update: %s', 'mxchat'),
639 esc_html($pdf_status['last_update'])
640 );
641 ?>
642 </p>
643 </div>
644 <?php endif; ?>
645
646 <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
647 <div class="mxchat-process-status sitemap" role="alert" aria-live="polite">
648 <h3><?php esc_html_e('Sitemap Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
649 <p>
650 <?php
651 printf(
652 /* translators: 1: Processed URLs, 2: Total URLs, 3: Percentage complete */
653 esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
654 absint($sitemap_status['processed_urls']),
655 absint($sitemap_status['total_urls']),
656 absint($sitemap_status['percentage'])
657 );
658 ?>
659 </p>
660 <p>
661 <?php
662 printf(
663 /* translators: %s: Current status */
664 esc_html__('Status: %s', 'mxchat'),
665 esc_html(ucfirst($sitemap_status['status']))
666 );
667 ?>
668 </p>
669 <p>
670 <?php
671 printf(
672 /* translators: %s: Time since last update */
673 esc_html__('Last update: %s', 'mxchat'),
674 esc_html($sitemap_status['last_update'])
675 );
676 ?>
677 </p>
678 </div>
679 <?php endif; ?>
680
681 <?php if ($is_processing) : ?>
682 <form id="mxchat-stop-form" method="post" class="mxchat-stop-form"
683 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
684
685 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
686
687 <input type="submit"
688 name="stop_processing"
689 value="<?php esc_attr_e('Stop Processing', 'mxchat'); ?>"
690 class="button button-secondary"
691 />
692 </form>
693 <?php endif; ?>
694
695 </div>
696
697 <!-- Search Knowledge Form -->
698 <div class="mxchat-grid-item">
699 <h2>Search Knowledge</h2>
700 <form method="get" id="knowledge-search">
701 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
702 <input type="hidden" name="page" value="mxchat-prompts" />
703 <div class="mxchat-search-group">
704 <input type="text" name="search" placeholder="Search Knowledge" value="<?php echo esc_attr($search_query); ?>" />
705 <input type="submit" value="Search" class="button button-primary" />
706 </div>
707 </form>
708 </div>
709 </div>
710
711 <!-- Table navigation with Delete All Button -->
712 <div class="tablenav">
713 <div class="tablenav-pages">
714 <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
715
716 <!-- Delete All Prompts Button -->
717 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>" style="display: inline;" onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all prompts? This action cannot be undone.', 'mxchat'); ?>');">
718 <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
719 <input type="submit" name="delete_all_prompts" value="<?php esc_attr_e('Delete All Knowledge', 'mxchat'); ?>" class="button mxchat-delete-all" />
720 </form>
721
722 <?php
723 // Display pagination links
724 $page_links = paginate_links(array(
725 'base' => add_query_arg(array('paged' => '%#%', 'search' => urlencode($search_query), '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')), admin_url('admin.php?page=mxchat-prompts')),
726 'format' => '',
727 'prev_text' => __('&laquo; Previous'),
728 'next_text' => __('Next &raquo;'),
729 'total' => $total_pages,
730 'current' => $current_page,
731 ));
732
733 if ($page_links) {
734 echo '<div class="tablenav-pages">' . wp_kses_post($page_links) . '</div>';
735 }
736 ?>
737 </div>
738 </div>
739
740 <!-- Prompts Table -->
741 <table class="mxchat-table">
742 <thead>
743 <tr>
744 <th>ID</th>
745 <th>Article Content</th>
746 <th>URL</th>
747 <th>Actions</th>
748 </tr>
749 </thead>
750 <tbody>
751 <?php if ($prompts) : ?>
752 <?php foreach ($prompts as $prompt) : ?>
753 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
754 <td><?php echo esc_html($prompt->id); ?></td>
755 <td class="mxchat_article_content_dashboard">
756 <span class="content-view"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></span>
757 <textarea class="content-edit" style="display:none;"><?php echo esc_textarea($prompt->article_content); ?></textarea>
758 </td>
759 <td class="mxchat_article_url_dashboard">
760 <span class="url-view">
761 <?php if (!empty($prompt->source_url)) : ?>
762 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank"><?php echo esc_html($prompt->source_url); ?></a>
763 <?php else : ?>
764 N/A
765 <?php endif; ?>
766 </span>
767 <input type="url" class="url-edit" value="<?php echo esc_attr($prompt->source_url); ?>" style="display:none;" />
768 </td>
769 <td>
770 <button class="button edit-button" data-id="<?php echo esc_attr($prompt->id); ?>">Edit</button>
771 <button class="button save-button" data-id="<?php echo esc_attr($prompt->id); ?>" style="display:none;">Save</button>
772 <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>
773 </td>
774 </tr>
775 <?php endforeach; ?>
776 <?php else : ?>
777 <tr>
778 <td colspan="4"><?php esc_html_e('No prompts found.', 'mxchat'); ?></td>
779 </tr>
780 <?php endif; ?>
781 </tbody>
782 </table>
783 </div>
784 <?php
785 }
786
787
788 // Delete All Prompts
789 public function mxchat_handle_delete_all_prompts() {
790 // Verify nonce
791 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
792 wp_die(__('Nonce verification failed.', 'mxchat'));
793 }
794
795 // Check permissions
796 if (!current_user_can('manage_options')) {
797 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
798 }
799
800 global $wpdb;
801 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
802
803 // Delete all prompts from the table
804 $wpdb->query("DELETE FROM {$table_name}");
805
806 // Clear relevant cache
807 wp_cache_delete('all_prompts', 'mxchat_prompts');
808
809 // Redirect back with a success message
810 $redirect_url = add_query_arg(array(
811 'page' => 'mxchat-prompts',
812 'all_deleted' => 'true'
813 ), admin_url('admin.php'));
814
815 wp_safe_redirect($redirect_url);
816 exit;
817 }
818
819 // Single Prompt Deletion
820 public function mxchat_handle_delete_prompt() {
821 // Sanitize and validate nonce
822 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
823 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
824 wp_die('Nonce verification failed.');
825 }
826
827 // Check permissions
828 if (!current_user_can('manage_options')) {
829 wp_die('You do not have sufficient permissions to delete prompts.');
830 }
831
832 // Validate and sanitize ID parameter
833 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
834 if ($id <= 0) {
835 wp_die('Invalid prompt ID.');
836 }
837
838 global $wpdb;
839 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
840
841 // Clear cache and delete prompt
842 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
843 $wpdb->delete($table_name, array('id' => $id), array('%d'));
844
845 wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
846 exit;
847 }
848
849
850
851
852 public function mxchat_generate_embedding($text) {
853 $options = get_option('mxchat_options');
854 $api_key = $options['api_key'] ?? 'default_api_key';
855
856 $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
857 'body' => wp_json_encode(array(
858 'model' => 'text-embedding-ada-002',
859 'input' => $text
860 )),
861 'headers' => array(
862 'Authorization' => 'Bearer ' . $api_key,
863 'Content-Type' => 'application/json'
864 ),
865 ));
866
867 if (is_wp_error($response)) {
868 return null;
869 }
870
871 $response_data = json_decode(wp_remote_retrieve_body($response), true);
872 return $response_data['data'][0]['embedding'] ?? null;
873 }
874
875 public function mxchat_delete_chat_history() {
876 if (!current_user_can('manage_options')) {
877 echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
878 wp_die();
879 }
880
881 check_ajax_referer('mxchat_delete_chat_history', 'security');
882
883 global $wpdb;
884 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
885
886 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
887 foreach ($_POST['delete_session_ids'] as $session_id) {
888 $session_id_sanitized = sanitize_text_field($session_id);
889
890 // Clear relevant cache before deletion
891 $cache_key = 'chat_session_' . $session_id_sanitized;
892 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
893
894 // Perform the deletion
895 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
896 }
897
898 // Optionally, clear a general cache if you have one
899 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
900
901 echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
902 } else {
903 echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
904 }
905
906 wp_die();
907 }
908
909
910 public function mxchat_save_inline_prompt() {
911 // Check for nonce security
912 check_ajax_referer('mxchat_save_inline_nonce');
913
914 // Verify permissions
915 if (!current_user_can('manage_options')) {
916 wp_send_json_error('Permission denied.');
917 return;
918 }
919
920 global $wpdb;
921 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
922
923 // Validate and sanitize input data
924 $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
925 $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
926 $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
927
928 if ($prompt_id > 0 && !empty($article_content)) {
929 // Re-generate the embedding vector for the updated content
930 $embedding_vector = $this->mxchat_generate_embedding($article_content);
931
932 if (is_array($embedding_vector)) {
933 // Serialize the embedding vector before storing it
934 $embedding_vector_serialized = serialize($embedding_vector);
935
936 // Update the prompt in the database
937 $updated = $wpdb->update(
938 $table_name,
939 array(
940 'article_content' => $article_content,
941 'embedding_vector' => $embedding_vector_serialized,
942 'source_url' => $article_url,
943 ),
944 array('id' => $prompt_id),
945 array('%s', '%s', '%s'),
946 array('%d')
947 );
948
949 if ($updated !== false) {
950 wp_send_json_success();
951 } else {
952 wp_send_json_error('Database update failed.');
953 }
954 } else {
955 wp_send_json_error('Embedding generation failed.');
956 }
957 } else {
958 wp_send_json_error('Invalid data.');
959 }
960 }
961
962
963 public function mxchat_fetch_chat_history() {
964 global $wpdb;
965 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
966
967 // Check if the current user has sufficient permissions
968 if (!current_user_can('manage_options')) {
969 echo esc_html__('You do not have sufficient permissions to view this page.', 'mxchat');
970 wp_die();
971 }
972
973 // Fetch chat transcripts from the database, ordered by timestamp
974 $chat_transcripts = $wpdb->get_results(
975 $wpdb->prepare("SELECT * FROM {$table_name} ORDER BY timestamp ASC")
976 );
977
978 // If no transcripts are available, display a message
979 if (empty($chat_transcripts)) {
980 echo esc_html__('No chat history available.', 'mxchat');
981 wp_die();
982 }
983
984 ob_start();
985 $current_session_id = '';
986
987 foreach ($chat_transcripts as $transcript) {
988 // Start a new session block if session ID changes
989 if ($current_session_id !== $transcript->session_id) {
990 if ($current_session_id !== '') {
991 echo '</div>'; // Close the previous session block
992 }
993 $current_session_id = sanitize_text_field($transcript->session_id);
994 echo '<div class="chat-session">';
995 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>';
996 }
997
998 // Format the timestamp for display
999 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
1000
1001 // Determine the role to display (user identifier or email for users, bot for the AI)
1002 $role = $transcript->role;
1003 if ($role === 'user') {
1004 // Sanitize email if available
1005 if (!empty($transcript->user_email)) {
1006 $role = sanitize_email($transcript->user_email);
1007 } else {
1008 // Sanitize user identifier, anonymize if it's an IP address
1009 $user_identifier = sanitize_text_field($transcript->user_identifier);
1010
1011 // Check if the user identifier is an IP address and anonymize it
1012 if (filter_var($user_identifier, FILTER_VALIDATE_IP)) {
1013 $role = preg_replace('/\.\d+$/', '.xxx', $user_identifier); // Mask the last octet
1014 } else {
1015 $role = $user_identifier; // If it's not an IP, just display the identifier
1016 }
1017 }
1018 }
1019
1020 // Output the chat message
1021 echo '<div class="chat-message">';
1022 echo '<strong>' . esc_html($role) . ' (' . esc_html($formatted_timestamp) . '):</strong> ';
1023 echo wp_kses_post($transcript->message);
1024 echo '</div>';
1025 }
1026
1027 // Close the final session block
1028 echo '</div>';
1029
1030 $output = ob_get_clean();
1031
1032 echo $output;
1033 wp_die();
1034 }
1035
1036
1037 public function mxchat_create_activation_page() {
1038 $license_status = get_option('mxchat_license_status', 'inactive');
1039 $license_error = get_option('mxchat_license_error', '');
1040 ?>
1041 <div class="wrap mxchat-admin">
1042 <h2>MxChat Pro: Activation</h2>
1043 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
1044 <div class="error notice">
1045 <p><?php echo esc_html($license_error); ?></p>
1046 </div>
1047 <?php endif; ?>
1048
1049 <form id="mxchat-activation-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
1050 <table class="form-table">
1051 <tr valign="top">
1052 <th scope="row">Email Address</th>
1053 <td>
1054 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text" required />
1055 </td>
1056 </tr>
1057 <tr valign="top">
1058 <th scope="row">Activation Key</th>
1059 <td>
1060 <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text" required />
1061 </td>
1062 </tr>
1063 </table>
1064 <?php if ($license_status !== 'active'): ?>
1065 <button type="submit" id="activate_license_button" class="button button-primary"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
1066 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
1067 <?php endif; ?>
1068 </form>
1069
1070 <!-- License Status Display -->
1071 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
1072 </div>
1073 <?php
1074 }
1075
1076 public function mxchat_intents_page_html() {
1077 if ( ! current_user_can( 'manage_options' ) ) {
1078 return;
1079 }
1080
1081 // Fetch existing intents with pagination and filtering
1082 global $wpdb;
1083 $table_name = $wpdb->prefix . 'mxchat_intents';
1084 $page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
1085 $per_page = 20;
1086 $offset = ( $page - 1 ) * $per_page;
1087
1088 // Build the WHERE clause based on filters
1089 $where = '1=1';
1090 $search_term = isset( $_GET['s'] ) ? trim( $_GET['s'] ) : '';
1091 $callback_filter = isset( $_GET['callback_filter'] ) ? sanitize_text_field( $_GET['callback_filter'] ) : '';
1092
1093 if ( $search_term ) {
1094 $search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
1095 $where .= $wpdb->prepare( ' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like );
1096 }
1097
1098 if ( $callback_filter ) {
1099 $where .= $wpdb->prepare( ' AND callback_function = %s', $callback_filter );
1100 }
1101
1102 // Get total count for pagination
1103 $total_intents = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE $where" );
1104 $total_pages = ceil( $total_intents / $per_page );
1105
1106 // Fetch intents with pagination and filters
1107 $intents = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset ) );
1108
1109 // Get available callback functions with 'pro_only' flag
1110 $available_callbacks = $this->mxchat_get_available_callbacks();
1111 ?>
1112
1113 <div class="wrap mxchat-admin">
1114 <!-- Add Intent Form -->
1115 <form id="mxchat-add-intent-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
1116 <input type="hidden" name="action" value="mxchat_add_intent">
1117 <?php wp_nonce_field( 'mxchat_add_intent_nonce' ); ?>
1118 <h2><?php esc_html_e( 'Add New Intent', 'mxchat' ); ?></h2>
1119 <p class="description">
1120 <?php esc_html_e( 'We highly encourage users to quickly read our ', 'mxchat' ); ?>
1121 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
1122 <?php esc_html_e( 'documentation', 'mxchat' ); ?>
1123 </a>
1124 <?php esc_html_e( ' to better understand intents. Some intents require setup in the Integration tab.', 'mxchat' ); ?>
1125 </p>
1126
1127 <div class="mxchat-form-group">
1128 <label for="intent_label"><?php esc_html_e( 'Intent Label (For your reference only)', 'mxchat' ); ?></label>
1129 <input name="intent_label" type="text" id="intent_label" class="regular-text" required
1130 placeholder="Example Email Capture: Newsletter Signup">
1131 </div>
1132 <div class="mxchat-form-group">
1133 <label for="phrases"><?php esc_html_e( 'Phrases (comma-separated)', 'mxchat' ); ?></label>
1134 <textarea name="phrases" id="phrases" rows="5" class="large-text" required
1135 placeholder="Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed"></textarea>
1136 </div>
1137 <div class="mxchat-form-group">
1138 <label for="callback_function"><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></label>
1139 <select name="callback_function" id="callback_function" required>
1140 <option value=""><?php esc_html_e( 'Select a Callback', 'mxchat' ); ?></option>
1141 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1142 <?php
1143 $label = $callback_data['label'];
1144 $pro_only = $callback_data['pro_only'];
1145 $disabled = ( ! $this->is_activated && $pro_only ) ? 'disabled' : '';
1146 $label_suffix = ( ! $this->is_activated && $pro_only ) ? ' (Pro Only)' : '';
1147 ?>
1148 <option value="<?php echo esc_attr( $function ); ?>" <?php echo $disabled; ?>>
1149 <?php echo esc_html( $label . $label_suffix ); ?>
1150 </option>
1151 <?php endforeach; ?>
1152 </select>
1153 </div>
1154 <button type="submit" class="button button-primary submit-content-button">
1155 <?php esc_html_e( 'Add Intent', 'mxchat' ); ?>
1156 </button>
1157 <div id="mxchat-intent-loading" style="display: none;"></div>
1158 <div id="mxchat-intent-loading-text" style="display: none;">
1159 <?php esc_html_e( 'Saving intent, please wait...', 'mxchat' ); ?>
1160 </div>
1161
1162 </form>
1163
1164 <!-- Filter Form -->
1165 <form method="get" action="">
1166 <input type="hidden" name="page" value="mxchat-intents">
1167 <div class="mxchat-search-group">
1168 <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e( 'Search Intents', 'mxchat' ); ?>" value="<?php echo esc_attr( $search_term ); ?>">
1169 <select name="callback_filter" id="mxchat-callback-filter">
1170 <option value=""><?php esc_html_e( 'All Callbacks', 'mxchat' ); ?></option>
1171 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1172 <?php $label = $callback_data['label']; ?>
1173 <option value="<?php echo esc_attr( $function ); ?>" <?php selected( $callback_filter, $function ); ?>><?php echo esc_html( $label ); ?></option>
1174 <?php endforeach; ?>
1175 </select>
1176 <button type="submit" class="button"><?php esc_html_e( 'Filter', 'mxchat' ); ?></button>
1177 </div>
1178 </form>
1179
1180 <!-- Intents Table -->
1181 <table class="wp-list-table mxchat-intents-table widefat fixed striped">
1182 <thead>
1183 <tr>
1184 <th><?php esc_html_e( 'Intent Label', 'mxchat' ); ?></th>
1185 <th><?php esc_html_e( 'Phrases', 'mxchat' ); ?></th>
1186 <th><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></th>
1187 <th><?php esc_html_e( 'Similarity Threshold', 'mxchat' ); ?></th>
1188 <th><?php esc_html_e( 'Actions', 'mxchat' ); ?></th>
1189 </tr>
1190 </thead>
1191 <tbody>
1192 <?php if ( $intents ) : ?>
1193 <?php foreach ( $intents as $intent ) : ?>
1194 <?php
1195 $callback_function = $intent->callback_function;
1196 $callback_label = isset( $available_callbacks[ $callback_function ]['label'] ) ? $available_callbacks[ $callback_function ]['label'] : $callback_function;
1197 $threshold_value = isset( $intent->similarity_threshold ) ? round( $intent->similarity_threshold * 100 ) : 85;
1198 ?>
1199 <tr>
1200 <td><?php echo esc_html( $intent->intent_label ); ?></td>
1201 <td><?php echo esc_html( $intent->phrases ); ?></td>
1202 <td><?php echo esc_html( $callback_label ); ?></td>
1203 <!-- Similarity Threshold Column -->
1204 <td>
1205 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
1206 <?php wp_nonce_field( 'mxchat_update_intent_threshold_nonce' ); ?>
1207 <input type="hidden" name="action" value="mxchat_update_intent_threshold">
1208 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1209 <input type="range" name="intent_threshold" id="intent_threshold_<?php echo esc_attr( $intent->id ); ?>" min="70" max="95" value="<?php echo esc_attr( $threshold_value ); ?>" oninput="document.getElementById('threshold_output_<?php echo esc_attr( $intent->id ); ?>').value = this.value + '%'">
1210 <output id="threshold_output_<?php echo esc_attr( $intent->id ); ?>"><?php echo esc_html( $threshold_value ); ?>%</output>
1211 </td>
1212 <!-- Actions Column -->
1213 <td>
1214 <button type="submit" class="button button-primary mxchat-save-button">
1215 <?php esc_html_e( 'Save', 'mxchat' ); ?>
1216 </button>
1217 </form>
1218 <!-- Delete Form -->
1219 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" onsubmit="return confirm('<?php esc_attr_e( 'Are you sure you want to delete this intent?', 'mxchat' ); ?>');">
1220 <?php wp_nonce_field( 'mxchat_delete_intent_nonce' ); ?>
1221 <input type="hidden" name="action" value="mxchat_delete_intent">
1222 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1223 <button type="submit" class="button mxchat-delete-all">
1224 <?php esc_html_e( 'Delete', 'mxchat' ); ?>
1225 </button>
1226 </form>
1227 </td>
1228 </tr>
1229 <?php endforeach; ?>
1230 <?php else : ?>
1231 <tr>
1232 <td colspan="5"><?php esc_html_e( 'No intents found.', 'mxchat' ); ?></td>
1233 </tr>
1234 <?php endif; ?>
1235 </tbody>
1236 </table>
1237 </div>
1238 <?php
1239 }
1240
1241
1242 public function mxchat_handle_update_intent_threshold() {
1243 if ( ! current_user_can( 'manage_options' ) ) {
1244 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1245 }
1246
1247 check_admin_referer('mxchat_update_intent_threshold_nonce');
1248
1249 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
1250 global $wpdb;
1251 $table_name = $wpdb->prefix . 'mxchat_intents';
1252 $intent_id = intval($_POST['intent_id']);
1253 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
1254 $similarity_threshold = $threshold_percentage / 100;
1255
1256 $wpdb->update(
1257 $table_name,
1258 ['similarity_threshold' => $similarity_threshold],
1259 ['id' => $intent_id],
1260 ['%f'],
1261 ['%d']
1262 );
1263 }
1264
1265 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1266 exit;
1267 }
1268
1269
1270 public function mxchat_handle_add_intent() {
1271 if ( ! current_user_can( 'manage_options' ) ) {
1272 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1273 }
1274
1275 check_admin_referer('mxchat_add_intent_nonce');
1276
1277 global $wpdb;
1278 $table_name = $wpdb->prefix . 'mxchat_intents';
1279
1280 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
1281 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
1282 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
1283 $default_threshold = 0.85;
1284
1285 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
1286 wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
1287 }
1288
1289 $available_callbacks = $this->mxchat_get_available_callbacks();
1290
1291 if (!array_key_exists($callback_function, $available_callbacks)) {
1292 wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
1293 }
1294
1295 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
1296 if ($is_pro_only && !$this->is_activated) {
1297 wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
1298 }
1299
1300 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
1301
1302 if (empty($phrases_array)) {
1303 wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
1304 }
1305
1306 $vectors = [];
1307 foreach ($phrases_array as $phrase) {
1308 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1309 if (is_array($embedding_vector)) {
1310 $vectors[] = $embedding_vector;
1311 } else {
1312 wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
1313 }
1314 }
1315
1316 if (!empty($vectors)) {
1317 $combined_vector = $this->mxchat_average_vectors($vectors);
1318 $serialized_vector = maybe_serialize($combined_vector);
1319
1320 $result = $wpdb->insert($table_name, [
1321 'intent_label' => $intent_label,
1322 'phrases' => implode(', ', $phrases_array),
1323 'embedding_vector' => $serialized_vector,
1324 'callback_function' => $callback_function,
1325 'similarity_threshold' => $default_threshold,
1326 ]);
1327
1328 if ($result === false) {
1329 wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
1330 }
1331 } else {
1332 wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
1333 }
1334
1335 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1336 exit;
1337 }
1338
1339
1340
1341
1342
1343
1344 private function mxchat_get_available_callbacks() {
1345 return [
1346 'mxchat_handle_email_capture' => [
1347 'label' => 'Email Capture',
1348 'pro_only' => false,
1349 ],
1350 'mxchat_handle_product_inquiry' => [
1351 'label' => 'Show Product Card',
1352 'pro_only' => true,
1353 ],
1354 'mxchat_handle_order_history' => [
1355 'label' => 'Order History',
1356 'pro_only' => true,
1357 ],
1358 'mxchat_generate_image' => [
1359 'label' => 'Generate Image',
1360 'pro_only' => true,
1361 ],
1362 'mxchat_handle_search_request' => [
1363 'label' => 'Brave Web Search',
1364 'pro_only' => false,
1365 ],
1366 'mxchat_handle_image_search_request' => [
1367 'label' => 'Brave Image Search',
1368 'pro_only' => false,
1369 ],
1370 'mxchat_handle_add_to_cart_intent' => [
1371 'label' => 'Add to Cart',
1372 'pro_only' => true,
1373 ],
1374 'mxchat_handle_checkout_intent' => [
1375 'label' => 'Proceed to Checkout',
1376 'pro_only' => true,
1377 ],
1378 'mxchat_handle_pdf_discussion' => [
1379 'label' => 'Chat with PDF',
1380 'pro_only' => true,
1381 ],
1382 'mxchat_handle_product_recommendations' => [
1383 'label' => 'Product Recommendations',
1384 'pro_only' => true,
1385 ],
1386 'mxchat_live_agent_handover' => [
1387 'label' => 'Live Agent',
1388 'pro_only' => true,
1389 ],
1390 'mxchat_handle_switch_to_chatbot_intent' => [
1391 'label' => 'Back to Chatbot',
1392 'pro_only' => true,
1393 ],
1394 ];
1395 }
1396
1397
1398 private function mxchat_average_vectors($vectors) {
1399 $vector_length = count($vectors[0]);
1400 $sum_vector = array_fill(0, $vector_length, 0);
1401
1402 foreach ($vectors as $vector) {
1403 for ($i = 0; $i < $vector_length; $i++) {
1404 $sum_vector[$i] += $vector[$i];
1405 }
1406 }
1407
1408 // Divide each component by the number of vectors to get the average
1409 $num_vectors = count($vectors);
1410 for ($i = 0; $i < $vector_length; $i++) {
1411 $sum_vector[$i] /= $num_vectors;
1412 }
1413
1414 return $sum_vector;
1415 }
1416
1417 public function mxchat_handle_delete_intent() {
1418 if ( ! current_user_can( 'manage_options' ) ) {
1419 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1420 }
1421
1422 check_admin_referer('mxchat_delete_intent_nonce');
1423
1424 if (isset($_POST['intent_id'])) {
1425 global $wpdb;
1426 $table_name = $wpdb->prefix . 'mxchat_intents';
1427 $intent_id = intval($_POST['intent_id']);
1428
1429 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
1430 }
1431
1432 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1433 exit;
1434 }
1435
1436
1437 public function mxchat_page_init() {
1438 // Register settings
1439 register_setting(
1440 'mxchat_option_group',
1441 'mxchat_options',
1442 array($this, 'mxchat_sanitize')
1443 );
1444
1445 // Chatbot Settings Section
1446 add_settings_section(
1447 'mxchat_chatbot_section',
1448 'Chatbot Settings',
1449 null,
1450 'mxchat-chatbot'
1451 );
1452
1453 // Registering fields for the Chatbot Settings section
1454 add_settings_field(
1455 'api_key',
1456 'OpenAI API Key',
1457 array($this, 'api_key_callback'),
1458 'mxchat-chatbot',
1459 'mxchat_chatbot_section'
1460 );
1461
1462 add_settings_field(
1463 'xai_api_key',
1464 'X.AI API Key',
1465 array($this, 'xai_api_key_callback'),
1466 'mxchat-chatbot',
1467 'mxchat_chatbot_section'
1468 );
1469
1470 add_settings_field(
1471 'claude_api_key',
1472 'Claude API Key',
1473 array($this, 'claude_api_key_callback'),
1474 'mxchat-chatbot',
1475 'mxchat_chatbot_section'
1476 );
1477
1478
1479 add_settings_field(
1480 'system_prompt_instructions',
1481 'AI Instructions',
1482 array($this, 'system_prompt_instructions_callback'),
1483 'mxchat-chatbot',
1484 'mxchat_chatbot_section'
1485 );
1486
1487 add_settings_field(
1488 'model',
1489 'Model',
1490 array($this, 'mxchat_model_callback'),
1491 'mxchat-chatbot',
1492 'mxchat_chatbot_section'
1493 );
1494
1495 add_settings_field(
1496 'top_bar_title',
1497 'Top Bar Title',
1498 array($this, 'mxchat_top_bar_title_callback'),
1499 'mxchat-chatbot',
1500 'mxchat_chatbot_section'
1501 );
1502
1503 add_settings_field(
1504 'intro_message',
1505 'Introductory Message',
1506 array($this, 'mxchat_intro_message_callback'),
1507 'mxchat-chatbot',
1508 'mxchat_chatbot_section'
1509 );
1510
1511 add_settings_field(
1512 'input_copy',
1513 'Input Copy',
1514 array($this, 'mxchat_input_copy_callback'),
1515 'mxchat-chatbot',
1516 'mxchat_chatbot_section'
1517 );
1518
1519 add_settings_field(
1520 'rate_limit_logged_in',
1521 __('Rate Limit for Logged-in Users', 'mxchat'),
1522 array($this, 'mxchat_rate_limit_logged_in_callback'),
1523 'mxchat-chatbot',
1524 'mxchat_chatbot_section'
1525 );
1526
1527 add_settings_field(
1528 'rate_limit_logged_out',
1529 __('Rate Limit for Logged-out Users', 'mxchat'),
1530 array($this, 'mxchat_rate_limit_logged_out_callback'),
1531 'mxchat-chatbot',
1532 'mxchat_chatbot_section'
1533 );
1534
1535 add_settings_field(
1536 'rate_limit_message',
1537 'Rate Limit Message',
1538 array($this, 'mxchat_rate_limit_message_callback'),
1539 'mxchat-chatbot',
1540 'mxchat_chatbot_section'
1541 );
1542
1543 add_settings_field(
1544 'pre_chat_message',
1545 'Pre-Chat Message',
1546 array($this, 'mxchat_pre_chat_message_callback'),
1547 'mxchat-chatbot',
1548 'mxchat_chatbot_section'
1549 );
1550
1551 add_settings_field(
1552 'append_to_body',
1553 'Append Chat Widget to Body',
1554 array($this, 'mxchat_append_to_body_callback'),
1555 'mxchat-chatbot',
1556 'mxchat_chatbot_section'
1557 );
1558
1559 add_settings_field(
1560 'privacy_toggle',
1561 'Toggle Privacy Notice',
1562 array($this, 'mxchat_privacy_toggle_callback'),
1563 'mxchat-chatbot',
1564 'mxchat_chatbot_section'
1565 );
1566
1567 add_settings_field(
1568 'complianz_toggle',
1569 'Enable Complianz',
1570 array($this, 'mxchat_complianz_toggle_callback'),
1571 'mxchat-chatbot',
1572 'mxchat_chatbot_section'
1573 );
1574
1575 add_settings_field(
1576 'link_target_toggle',
1577 'Open Links in a New Tab',
1578 array($this, 'mxchat_link_target_toggle_callback'),
1579 'mxchat-chatbot',
1580 'mxchat_chatbot_section'
1581 );
1582
1583 add_settings_field(
1584 'chat_persistence_toggle',
1585 'Enable Chat Persistence',
1586 array($this, 'mxchat_chat_persistence_toggle_callback'),
1587 'mxchat-chatbot',
1588 'mxchat_chatbot_section'
1589 );
1590
1591 add_settings_field(
1592 'popular_question_1',
1593 'Popular Question 1',
1594 array($this, 'mxchat_popular_question_1_callback'),
1595 'mxchat-chatbot',
1596 'mxchat_chatbot_section'
1597 );
1598
1599 add_settings_field(
1600 'popular_question_2',
1601 'Popular Question 2',
1602 array($this, 'mxchat_popular_question_2_callback'),
1603 'mxchat-chatbot',
1604 'mxchat_chatbot_section'
1605 );
1606
1607 add_settings_field(
1608 'popular_question_3',
1609 'Popular Question 3',
1610 array($this, 'mxchat_popular_question_3_callback'),
1611 'mxchat-chatbot',
1612 'mxchat_chatbot_section'
1613 );
1614
1615 add_settings_field(
1616 'additional_popular_questions',
1617 'Additional Popular Questions',
1618 array($this, 'mxchat_additional_popular_questions_callback'),
1619 'mxchat-chatbot',
1620 'mxchat_chatbot_section'
1621 );
1622
1623
1624 // WooCommerce Settings Section
1625 add_settings_section(
1626 'mxchat_woocommerce_section',
1627 'WooCommerce Settings',
1628 null,
1629 'mxchat-embed'
1630 );
1631
1632 // Loops Settings Section
1633 add_settings_section(
1634 'mxchat_loops_section',
1635 'Loops Settings',
1636 null,
1637 'mxchat-embed'
1638 );
1639
1640 // WooCommerce Settings Fields
1641 add_settings_field(
1642 'enable_woocommerce_integration',
1643 'Automatically Embed Products',
1644 array($this, 'mxchat_enable_woocommerce_integration_callback'),
1645 'mxchat-embed',
1646 'mxchat_woocommerce_section'
1647 );
1648
1649
1650 add_settings_field(
1651 'woocommerce_consumer_key',
1652 'WooCommerce Consumer Key',
1653 array($this, 'mxchat_woocommerce_consumer_key_callback'),
1654 'mxchat-embed',
1655 'mxchat_woocommerce_section'
1656 );
1657
1658 add_settings_field(
1659 'woocommerce_consumer_secret',
1660 'WooCommerce Consumer Secret',
1661 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
1662 'mxchat-embed',
1663 'mxchat_woocommerce_section'
1664 );
1665
1666 // Loops Settings Fields
1667 add_settings_field(
1668 'loops_api_key',
1669 'Loops API Key',
1670 array($this, 'mxchat_loops_api_key_callback'),
1671 'mxchat-embed',
1672 'mxchat_loops_section'
1673 );
1674
1675 add_settings_field(
1676 'loops_mailing_list',
1677 'Loops Mailing List',
1678 array($this, 'mxchat_loops_mailing_list_callback'),
1679 'mxchat-embed',
1680 'mxchat_loops_section'
1681 );
1682
1683 add_settings_field(
1684 'triggered_phrase_response',
1685 'Triggered Phrase Response',
1686 array($this, 'mxchat_triggered_phrase_response_callback'),
1687 'mxchat-embed',
1688 'mxchat_loops_section'
1689 );
1690
1691 add_settings_field(
1692 'email_capture_response',
1693 'Email Capture Response',
1694 array($this, 'mxchat_email_capture_response_callback'),
1695 'mxchat-embed',
1696 'mxchat_loops_section'
1697 );
1698
1699
1700 // Brave Search Settings Fields
1701 add_settings_section(
1702 'mxchat_brave_section',
1703 __('Brave Search Settings', 'mxchat'),
1704 array($this, 'mxchat_brave_section_callback'),
1705 'mxchat-embed'
1706 );
1707
1708 add_settings_field(
1709 'brave_api_key',
1710 __('Brave API Key', 'mxchat'),
1711 array($this, 'mxchat_brave_api_key_callback'),
1712 'mxchat-embed',
1713 'mxchat_brave_section'
1714 );
1715
1716 add_settings_field(
1717 'brave_image_count',
1718 __('Number of Images to Return', 'mxchat'),
1719 array($this, 'mxchat_brave_image_count_callback'),
1720 'mxchat-embed',
1721 'mxchat_brave_section'
1722 );
1723
1724 add_settings_field(
1725 'brave_safe_search',
1726 __('Safe Search', 'mxchat'),
1727 array($this, 'mxchat_brave_safe_search_callback'),
1728 'mxchat-embed',
1729 'mxchat_brave_section'
1730 );
1731
1732 add_settings_field(
1733 'brave_news_count',
1734 __('Number of News Articles', 'mxchat'),
1735 array($this, 'mxchat_brave_news_count_callback'),
1736 'mxchat-embed',
1737 'mxchat_brave_section'
1738 );
1739
1740 add_settings_field(
1741 'brave_country',
1742 __('Country', 'mxchat'),
1743 array($this, 'mxchat_brave_country_callback'),
1744 'mxchat-embed',
1745 'mxchat_brave_section'
1746 );
1747
1748 add_settings_field(
1749 'brave_language',
1750 __('Language', 'mxchat'),
1751 array($this, 'mxchat_brave_language_callback'),
1752 'mxchat-embed',
1753 'mxchat_brave_section'
1754 );
1755
1756
1757 // Chat with PDF Intent Settings Fields
1758 add_settings_section(
1759 'mxchat_pdf_intent_section',
1760 __('Toolbar Settings & Intents', 'mxchat'),
1761 array($this, 'mxchat_pdf_intent_section_callback'),
1762 'mxchat-embed'
1763 );
1764
1765 add_settings_field(
1766 'chat_toolbar_toggle',
1767 __('Show Chat Toolbar', 'mxchat'),
1768 array($this, 'mxchat_chat_toolbar_toggle_callback'),
1769 'mxchat-embed',
1770 'mxchat_pdf_intent_section'
1771 );
1772
1773
1774 add_settings_field(
1775 'pdf_intent_trigger_text',
1776 __('Intent Trigger Text', 'mxchat'),
1777 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
1778 'mxchat-embed',
1779 'mxchat_pdf_intent_section'
1780 );
1781
1782 add_settings_field(
1783 'pdf_intent_success_text',
1784 __('Success Text', 'mxchat'),
1785 array($this, 'mxchat_pdf_intent_success_text_callback'),
1786 'mxchat-embed',
1787 'mxchat_pdf_intent_section'
1788 );
1789
1790 add_settings_field(
1791 'pdf_intent_error_text',
1792 __('Error Text', 'mxchat'),
1793 array($this, 'mxchat_pdf_intent_error_text_callback'),
1794 'mxchat-embed',
1795 'mxchat_pdf_intent_section'
1796 );
1797
1798 // Add PDF Maximum Pages Field
1799 add_settings_field(
1800 'pdf_max_pages',
1801 __('Maximum PDF Pages', 'mxchat'),
1802 array($this, 'mxchat_pdf_max_pages_callback'),
1803 'mxchat-embed',
1804 'mxchat_pdf_intent_section'
1805 );
1806
1807
1808
1809
1810 // Live Agent Settings Fields
1811 add_settings_section(
1812 'mxchat_live_agent_section',
1813 __('Live Agent Settings', 'mxchat'),
1814 array($this, 'mxchat_live_agent_section_callback'),
1815 'mxchat-embed'
1816 );
1817
1818 // Live Agent Status Fields (add at top of live agent settings)
1819 add_settings_field(
1820 'live_agent_status',
1821 __('Live Agent Status', 'mxchat'),
1822 array($this, 'mxchat_live_agent_status_callback'),
1823 'mxchat-embed',
1824 'mxchat_live_agent_section'
1825 );
1826
1827 add_settings_field(
1828 'live_agent_notification_message',
1829 __('Notification Message', 'mxchat'),
1830 array($this, 'mxchat_live_agent_notification_message_callback'),
1831 'mxchat-embed',
1832 'mxchat_live_agent_section'
1833 );
1834
1835 add_settings_field(
1836 'live_agent_away_message',
1837 __('Away Message', 'mxchat'),
1838 array($this, 'mxchat_live_agent_away_message_callback'),
1839 'mxchat-embed',
1840 'mxchat_live_agent_section'
1841 );
1842
1843 add_settings_field(
1844 'live_agent_webhook_url',
1845 __('Slack Webhook URL', 'mxchat'),
1846 array($this, 'mxchat_live_agent_webhook_url_callback'),
1847 'mxchat-embed',
1848 'mxchat_live_agent_section'
1849 );
1850
1851 add_settings_field(
1852 'live_agent_secret_key',
1853 __('Slack Secret Key', 'mxchat'),
1854 array($this, 'mxchat_live_agent_secret_key_callback'),
1855 'mxchat-embed',
1856 'mxchat_live_agent_section'
1857 );
1858
1859 // Live Agent Integration Fields
1860 add_settings_field(
1861 'live_agent_bot_token',
1862 __('Slack Bot OAuth Token', 'mxchat'),
1863 array($this, 'mxchat_live_agent_bot_token_callback'),
1864 'mxchat-embed',
1865 'mxchat_live_agent_section'
1866 );
1867
1868
1869 // Theme Settings Section
1870 add_settings_section(
1871 'mxchat_theme_section',
1872 'Theme Settings',
1873 null,
1874 'mxchat-theme'
1875 );
1876
1877 add_settings_field(
1878 'close_button_color',
1879 'Close Button & Title Color',
1880 array($this, 'mxchat_close_button_color_callback'),
1881 'mxchat-theme',
1882 'mxchat_theme_section'
1883 );
1884
1885 add_settings_field(
1886 'chatbot_bg_color',
1887 'Chatbot Background Color',
1888 array($this, 'mxchat_chatbot_bg_color_callback'),
1889 'mxchat-theme',
1890 'mxchat_theme_section'
1891 );
1892
1893 add_settings_field(
1894 'user_message_bg_color',
1895 'User Message Background Color',
1896 array($this, 'mxchat_user_message_bg_color_callback'),
1897 'mxchat-theme',
1898 'mxchat_theme_section'
1899 );
1900
1901 add_settings_field(
1902 'user_message_font_color',
1903 'User Message Font Color',
1904 array($this, 'mxchat_user_message_font_color_callback'),
1905 'mxchat-theme',
1906 'mxchat_theme_section'
1907 );
1908
1909 add_settings_field(
1910 'bot_message_bg_color',
1911 'Bot Message Background Color',
1912 array($this, 'mxchat_bot_message_bg_color_callback'),
1913 'mxchat-theme',
1914 'mxchat_theme_section'
1915 );
1916
1917 add_settings_field(
1918 'bot_message_font_color',
1919 'Bot Message Font Color',
1920 array($this, 'mxchat_bot_message_font_color_callback'),
1921 'mxchat-theme',
1922 'mxchat_theme_section'
1923 );
1924
1925 add_settings_field(
1926 'top_bar_bg_color',
1927 'Top Bar Background Color',
1928 array($this, 'mxchat_top_bar_bg_color_callback'),
1929 'mxchat-theme',
1930 'mxchat_theme_section'
1931 );
1932
1933 add_settings_field(
1934 'send_button_font_color',
1935 'Send Button Color',
1936 array($this, 'mxchat_send_button_font_color_callback'),
1937 'mxchat-theme',
1938 'mxchat_theme_section'
1939 );
1940
1941 add_settings_field(
1942 'chat_input_font_color',
1943 'Chat Input Font Color',
1944 array($this, 'mxchat_chat_input_font_color_callback'),
1945 'mxchat-theme',
1946 'mxchat_theme_section'
1947 );
1948
1949 add_settings_field(
1950 'chatbot_background_color',
1951 'Floating Widget Background Color',
1952 array($this, 'mxchat_chatbot_background_color_callback'),
1953 'mxchat-theme',
1954 'mxchat_theme_section'
1955 );
1956
1957 add_settings_field(
1958 'icon_color',
1959 'Chatbot Icon Color',
1960 array($this, 'mxchat_icon_color_callback'),
1961 'mxchat-theme',
1962 'mxchat_theme_section'
1963 );
1964
1965 add_settings_field(
1966 'custom_icon',
1967 'Custom Chatbot Icon (PNG)',
1968 array($this, 'mxchat_custom_icon_callback'),
1969 'mxchat-theme',
1970 'mxchat_theme_section'
1971 );
1972
1973
1974 add_settings_field(
1975 'live_agent_message_bg_color',
1976 'Live Agent Background Color',
1977 array($this, 'mxchat_live_agent_message_bg_color_callback'),
1978 'mxchat-theme',
1979 'mxchat_theme_section'
1980 );
1981
1982 add_settings_field(
1983 'live_agent_message_font_color',
1984 'Live Agent Font Color',
1985 array($this, 'mxchat_live_agent_message_font_color_callback'),
1986 'mxchat-theme',
1987 'mxchat_theme_section'
1988 );
1989
1990 // Mode Indicator Background Color
1991 add_settings_field(
1992 'mode_indicator_bg_color',
1993 'Mode Indicator Background Color',
1994 array($this, 'mxchat_mode_indicator_bg_color_callback'),
1995 'mxchat-theme',
1996 'mxchat_theme_section'
1997 );
1998
1999 // Mode Indicator Font Color
2000 add_settings_field(
2001 'mode_indicator_font_color',
2002 'Mode Indicator Font Color',
2003 array($this, 'mxchat_mode_indicator_font_color_callback'),
2004 'mxchat-theme',
2005 'mxchat_theme_section'
2006 );
2007
2008 add_settings_field(
2009 'toolbar_icon_color',
2010 'Toolbar Icon Color',
2011 array($this, 'mxchat_toolbar_icon_color_callback'),
2012 'mxchat-theme',
2013 'mxchat_theme_section'
2014 );
2015
2016 // General Settings Section
2017 add_settings_section(
2018 'mxchat_general_section',
2019 'Frequently Asked Questions (FAQ)',
2020 null,
2021 'mxchat-general'
2022 );
2023
2024
2025 }
2026
2027 public function mxchat_handle_activate_license() {
2028 check_ajax_referer('mxchat_activate_license_nonce', 'security');
2029
2030 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
2031 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
2032
2033 if (empty($license_key) || empty($customer_email)) {
2034 wp_send_json_error('Email or License Key is missing');
2035 }
2036
2037 $product_id = 'MxChatPRO';
2038
2039 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
2040
2041 if (is_wp_error($response)) {
2042 wp_send_json_error('Activation failed due to a server error');
2043 }
2044
2045 $body = wp_remote_retrieve_body($response);
2046 $data = json_decode($body);
2047
2048 if ($data && isset($data->activated) && $data->activated) {
2049 update_option('mxchat_license_status', 'active');
2050 update_option('mxchat_pro_email', $customer_email);
2051 update_option('mxchat_activation_key', $license_key);
2052 wp_send_json_success();
2053 } else {
2054 $error_message = isset($data->error) ? $data->error : 'Activation failed';
2055 update_option('mxchat_license_status', 'inactive');
2056 update_option('mxchat_license_error', $error_message);
2057 wp_send_json_error($error_message);
2058 }
2059 }
2060
2061 public function mxchat_rate_limit_logged_in_callback() {
2062 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2063 $selected_rate_limit = isset($this->options['rate_limit_logged_in']) ? $this->options['rate_limit_logged_in'] : '100';
2064
2065 echo '<div class="pro-feature-wrapper active">';
2066 echo '<select id="rate_limit_logged_in" name="mxchat_options[rate_limit_logged_in]">';
2067 foreach ($rate_limits as $limit) {
2068 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2069 }
2070 echo '</select>';
2071 echo '<p class="description">Set the rate limit for logged-in users.</p>';
2072 echo '</div>';
2073 }
2074
2075 public function mxchat_rate_limit_logged_out_callback() {
2076 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2077 $selected_rate_limit = isset($this->options['rate_limit_logged_out']) ? $this->options['rate_limit_logged_out'] : '10';
2078
2079 echo '<div class="pro-feature-wrapper active">';
2080 echo '<select id="rate_limit_logged_out" name="mxchat_options[rate_limit_logged_out]">';
2081 foreach ($rate_limits as $limit) {
2082 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2083 }
2084 echo '</select>';
2085 echo '<p class="description">Set the rate limit for logged-out users.</p>';
2086 echo '</div>';
2087 }
2088
2089
2090 public function mxchat_rate_limit_message_callback() {
2091 // Remove the is_activated check and make it always enabled
2092 echo '<div class="pro-feature-wrapper active">';
2093 printf(
2094 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
2095 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
2096 );
2097 echo '</div>';
2098 }
2099
2100
2101 public function mxchat_enable_woocommerce_integration_callback() {
2102 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
2103 $disabled = $this->is_activated ? '' : 'disabled';
2104 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2105
2106 echo '<div class="' . esc_attr($class) . '">';
2107 echo '<label class="toggle-switch">';
2108 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
2109 echo '<span class="slider"></span>';
2110 echo '</label>';
2111
2112 if (!$this->is_activated) {
2113 echo '<div class="pro-feature-overlay">';
2114 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2115 echo '</div>';
2116 }
2117
2118 echo '</div>';
2119 }
2120
2121
2122
2123
2124 private function mxchat_add_option_field($id, $title, $callback = '') {
2125 add_settings_field(
2126 $id,
2127 $title,
2128 $callback ? array($this, $callback) : array($this, $id . '_callback'),
2129 'mxchat-max',
2130 'mxchat_setting_section_id',
2131 $id === 'model' ? ['label_for' => 'model'] : []
2132 );
2133 }
2134
2135
2136 public function api_key_callback() {
2137 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
2138 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
2139 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
2140 }
2141
2142 public function xai_api_key_callback() {
2143 // Check if the feature is activated (paid feature)
2144 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2145 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2146
2147 // Render the input field for the X.AI API key
2148 echo '<div class="' . esc_attr($class) . '">';
2149 printf(
2150 '<input type="password" id="xai_api_key" name="mxchat_options[xai_api_key]" value="%s" class="regular-text" %s />',
2151 isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
2152 $disabled
2153 );
2154 echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
2155
2156 // If the feature is not activated, show the overlay with a "Pro Only" message
2157 if (!$this->is_activated) {
2158 echo '<div class="pro-feature-overlay">';
2159 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2160 echo '</div>';
2161 }
2162
2163 echo '</div>';
2164 }
2165
2166 public function claude_api_key_callback() {
2167 // Check if the feature is activated (paid feature)
2168 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2169 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2170
2171 // Render the input field for the Claude API key
2172 echo '<div class="' . esc_attr($class) . '">';
2173 printf(
2174 '<input type="password" id="claude_api_key" name="mxchat_options[claude_api_key]" value="%s" class="regular-text" %s />',
2175 isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
2176 $disabled
2177 );
2178 echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
2179
2180 // If the feature is not activated, show the overlay with a "Pro Only" message
2181 if (!$this->is_activated) {
2182 echo '<div class="pro-feature-overlay">';
2183 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2184 echo '</div>';
2185 }
2186
2187 echo '</div>';
2188 }
2189
2190 public function mxchat_woocommerce_consumer_key_callback() {
2191 $disabled = $this->is_activated ? '' : 'disabled';
2192 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2193
2194 echo '<div class="' . esc_attr($class) . '">';
2195 printf(
2196 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
2197 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
2198 $disabled
2199 );
2200
2201 if (!$this->is_activated) {
2202 echo '<div class="pro-feature-overlay">';
2203 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2204 echo '</div>';
2205 }
2206
2207 echo '</div>';
2208 }
2209
2210 public function mxchat_woocommerce_consumer_secret_callback() {
2211 $disabled = $this->is_activated ? '' : 'disabled';
2212 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2213
2214 echo '<div class="' . esc_attr($class) . '">';
2215 printf(
2216 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
2217 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
2218 $disabled
2219 );
2220 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
2221
2222 if (!$this->is_activated) {
2223 echo '<div class="pro-feature-overlay">';
2224 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2225 echo '</div>';
2226 }
2227
2228 echo '</div>';
2229 }
2230
2231 public function mxchat_loops_api_key_callback() {
2232 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
2233
2234 echo '<div class="api-key-wrapper">';
2235 printf(
2236 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
2237 $loops_api_key
2238 );
2239 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
2240 echo '</div>';
2241 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
2242 }
2243
2244 public function mxchat_loops_mailing_list_callback() {
2245 // Retrieve Loops API key and lists
2246 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
2247 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
2248
2249 if ($loops_api_key) {
2250 // Fetch lists from Loops API
2251 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
2252
2253 if ($lists) {
2254 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
2255 foreach ($lists as $list) {
2256 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
2257 }
2258 echo '</select>';
2259 } else {
2260 echo '<p class="description">No lists found. Please verify your API Key.</p>';
2261 }
2262 } else {
2263 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
2264 }
2265 }
2266
2267 public function mxchat_triggered_phrase_response_callback() {
2268 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
2269 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
2270 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
2271 }
2272
2273 public function mxchat_email_capture_response_callback() {
2274 $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.';
2275 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
2276 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
2277 }
2278
2279
2280 public function mxchat_pre_chat_message_callback() {
2281 printf(
2282 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
2283 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
2284 );
2285 }
2286
2287 // Callback for AI Instructions textarea
2288 public function system_prompt_instructions_callback() {
2289 printf(
2290 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
2291 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
2292 );
2293 }
2294
2295 public function mxchat_model_callback() {
2296 $models = array(
2297 'X.AI Models' => array(
2298 'grok-beta' => 'grok-beta (Early Beta)'
2299 ),
2300 'Claude Models' => array(
2301 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
2302 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
2303 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
2304 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
2305 ),
2306 'OpenAI Models' => array(
2307 'gpt-4o' => 'GPT-4o (Recommended)',
2308 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
2309 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
2310 'gpt-4' => 'GPT-4 (High Intelligence)',
2311 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
2312 )
2313 );
2314
2315 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
2316
2317 echo '<select id="model" name="mxchat_options[model]">';
2318
2319 foreach ($models as $group_label => $group_models) {
2320 echo '<optgroup label="' . esc_attr($group_label) . '">';
2321
2322 foreach ($group_models as $model_value => $model_label) {
2323 // Disable if not activated for paid models (Claude or X.AI)
2324 $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
2325 $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
2326
2327 // Output the <option> element
2328 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
2329 }
2330
2331 echo '</optgroup>';
2332 }
2333
2334 echo '</select>';
2335 }
2336
2337
2338
2339 public function mxchat_top_bar_title_callback() {
2340 printf(
2341 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
2342 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
2343 );
2344 }
2345
2346 public function mxchat_intro_message_callback() {
2347 printf(
2348 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
2349 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
2350 );
2351 }
2352
2353 public function mxchat_input_copy_callback() {
2354 printf(
2355 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
2356 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
2357 );
2358 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
2359 }
2360
2361
2362
2363
2364
2365
2366 public function mxchat_close_button_color_callback() {
2367 $disabled = $this->is_activated ? '' : 'disabled';
2368
2369 echo '<div class="pro-feature-wrapper">';
2370 printf(
2371 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
2372 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
2373 esc_attr($disabled)
2374 );
2375
2376 if (!$this->is_activated) {
2377 echo '<div class="pro-feature-overlay">';
2378 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2379 echo '</div>';
2380 }
2381
2382 echo '</div>';
2383 }
2384
2385 public function mxchat_chatbot_bg_color_callback() {
2386 $disabled = $this->is_activated ? '' : 'disabled';
2387
2388 echo '<div class="pro-feature-wrapper">';
2389 printf(
2390 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
2391 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
2392 esc_attr($disabled)
2393 );
2394
2395 if (!$this->is_activated) {
2396 echo '<div class="pro-feature-overlay">';
2397 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2398 echo '</div>';
2399 }
2400
2401 echo '</div>';
2402 }
2403
2404 public function mxchat_user_message_bg_color_callback() {
2405 $disabled = $this->is_activated ? '' : 'disabled';
2406
2407 echo '<div class="pro-feature-wrapper">';
2408 printf(
2409 '<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 />',
2410 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
2411 esc_attr($disabled)
2412 );
2413
2414 if (!$this->is_activated) {
2415 echo '<div class="pro-feature-overlay">';
2416 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2417 echo '</div>';
2418 }
2419
2420 echo '</div>';
2421 }
2422
2423 public function mxchat_user_message_font_color_callback() {
2424 $disabled = $this->is_activated ? '' : 'disabled';
2425
2426 echo '<div class="pro-feature-wrapper">';
2427 printf(
2428 '<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 />',
2429 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
2430 esc_attr($disabled)
2431 );
2432
2433 if (!$this->is_activated) {
2434 echo '<div class="pro-feature-overlay">';
2435 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2436 echo '</div>';
2437 }
2438
2439 echo '</div>';
2440 }
2441
2442 public function mxchat_bot_message_bg_color_callback() {
2443 $disabled = $this->is_activated ? '' : 'disabled';
2444
2445 echo '<div class="pro-feature-wrapper">';
2446 printf(
2447 '<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 />',
2448 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
2449 esc_attr($disabled)
2450 );
2451
2452 if (!$this->is_activated) {
2453 echo '<div class="pro-feature-overlay">';
2454 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2455 echo '</div>';
2456 }
2457
2458 echo '</div>';
2459 }
2460
2461 public function mxchat_bot_message_font_color_callback() {
2462 $disabled = $this->is_activated ? '' : 'disabled';
2463
2464 echo '<div class="pro-feature-wrapper">';
2465 printf(
2466 '<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 />',
2467 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
2468 esc_attr($disabled)
2469 );
2470
2471 if (!$this->is_activated) {
2472 echo '<div class="pro-feature-overlay">';
2473 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2474 echo '</div>';
2475 }
2476
2477 echo '</div>';
2478 }
2479
2480 public function mxchat_live_agent_message_bg_color_callback() {
2481 $disabled = $this->is_activated ? '' : 'disabled';
2482
2483 echo '<div class="pro-feature-wrapper">';
2484 printf(
2485 '<input type="text" id="live_agent_message_bg_color" name="mxchat_options[live_agent_message_bg_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2486 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '',
2487 esc_attr($disabled)
2488 );
2489
2490 if (!$this->is_activated) {
2491 echo '<div class="pro-feature-overlay">';
2492 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2493 echo '</div>';
2494 }
2495
2496 echo '</div>';
2497 }
2498
2499 public function mxchat_live_agent_message_font_color_callback() {
2500 $disabled = $this->is_activated ? '' : 'disabled';
2501
2502 echo '<div class="pro-feature-wrapper">';
2503 printf(
2504 '<input type="text" id="live_agent_message_font_color" name="mxchat_options[live_agent_message_font_color]" value="%s" class="my-color-field" data-default-color="#333333" %s />',
2505 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '',
2506 esc_attr($disabled)
2507 );
2508
2509 if (!$this->is_activated) {
2510 echo '<div class="pro-feature-overlay">';
2511 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2512 echo '</div>';
2513 }
2514
2515 echo '</div>';
2516 }
2517
2518 // Mode Indicator Background Color Callback
2519 public function mxchat_mode_indicator_bg_color_callback() {
2520 $disabled = $this->is_activated ? '' : 'disabled';
2521
2522 echo '<div class="pro-feature-wrapper">';
2523 printf(
2524 '<input type="text" id="mode_indicator_bg_color" name="mxchat_options[mode_indicator_bg_color]" value="%s" class="my-color-field" data-default-color="#767676" %s />',
2525 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '',
2526 esc_attr($disabled)
2527 );
2528
2529 if (!$this->is_activated) {
2530 echo '<div class="pro-feature-overlay">';
2531 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2532 echo '</div>';
2533 }
2534
2535 echo '</div>';
2536 }
2537
2538 // Mode Indicator Font Color Callback
2539 public function mxchat_mode_indicator_font_color_callback() {
2540 $disabled = $this->is_activated ? '' : 'disabled';
2541
2542 echo '<div class="pro-feature-wrapper">';
2543 printf(
2544 '<input type="text" id="mode_indicator_font_color" name="mxchat_options[mode_indicator_font_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2545 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '',
2546 esc_attr($disabled)
2547 );
2548
2549 if (!$this->is_activated) {
2550 echo '<div class="pro-feature-overlay">';
2551 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2552 echo '</div>';
2553 }
2554
2555 echo '</div>';
2556 }
2557
2558 public function mxchat_toolbar_icon_color_callback() {
2559 $disabled = $this->is_activated ? '' : 'disabled';
2560
2561 echo '<div class="pro-feature-wrapper">';
2562 printf(
2563 '<input type="text" id="toolbar_icon_color" name="mxchat_options[toolbar_icon_color]" value="%s" class="my-color-field" data-default-color="#212121" %s />',
2564 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '',
2565 esc_attr($disabled)
2566 );
2567
2568 if (!$this->is_activated) {
2569 echo '<div class="pro-feature-overlay">';
2570 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2571 echo '</div>';
2572 }
2573
2574 echo '</div>';
2575 }
2576
2577
2578
2579
2580 public function mxchat_top_bar_bg_color_callback() {
2581 $disabled = $this->is_activated ? '' : 'disabled';
2582
2583 echo '<div class="pro-feature-wrapper">';
2584 printf(
2585 '<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 />',
2586 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
2587 esc_attr($disabled)
2588 );
2589
2590 if (!$this->is_activated) {
2591 echo '<div class="pro-feature-overlay">';
2592 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2593 echo '</div>';
2594 }
2595
2596 echo '</div>';
2597 }
2598
2599 public function mxchat_send_button_font_color_callback() {
2600 $disabled = $this->is_activated ? '' : 'disabled';
2601
2602 echo '<div class="pro-feature-wrapper">';
2603 printf(
2604 '<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 />',
2605 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
2606 esc_attr($disabled)
2607 );
2608
2609 if (!$this->is_activated) {
2610 echo '<div class="pro-feature-overlay">';
2611 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2612 echo '</div>';
2613 }
2614
2615 echo '</div>';
2616 }
2617
2618 public function mxchat_chatbot_background_color_callback() {
2619 $disabled = $this->is_activated ? '' : 'disabled';
2620
2621 echo '<div class="pro-feature-wrapper">';
2622 printf(
2623 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
2624 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
2625 esc_attr($disabled)
2626 );
2627
2628 if (!$this->is_activated) {
2629 echo '<div class="pro-feature-overlay">';
2630 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2631 echo '</div>';
2632 }
2633
2634 echo '</div>';
2635 }
2636
2637 public function mxchat_icon_color_callback() {
2638 $disabled = $this->is_activated ? '' : 'disabled';
2639
2640 echo '<div class="pro-feature-wrapper">';
2641 printf(
2642 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2643 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
2644 esc_attr($disabled)
2645 );
2646
2647 if (!$this->is_activated) {
2648 echo '<div class="pro-feature-overlay">';
2649 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2650 echo '</div>';
2651 }
2652
2653 echo '</div>';
2654 }
2655
2656 public function mxchat_custom_icon_callback() {
2657 $disabled = $this->is_activated ? '' : 'disabled';
2658 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
2659
2660 echo '<div class="pro-feature-wrapper">';
2661 printf(
2662 '<input type="url" id="custom_icon" name="mxchat_options[custom_icon]" value="%s" placeholder="Enter PNG URL" %s />',
2663 $custom_icon_url,
2664 esc_attr($disabled)
2665 );
2666 echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
2667
2668 if (!$this->is_activated) {
2669 echo '<div class="pro-feature-overlay">';
2670 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2671 echo '</div>';
2672 }
2673
2674 echo '</div>';
2675 }
2676
2677
2678 public function mxchat_chat_input_font_color_callback() {
2679 $disabled = $this->is_activated ? '' : 'disabled';
2680
2681 echo '<div class="pro-feature-wrapper">';
2682 printf(
2683 '<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 />',
2684 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
2685 esc_attr($disabled)
2686 );
2687
2688 if (!$this->is_activated) {
2689 echo '<div class="pro-feature-overlay">';
2690 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2691 echo '</div>';
2692 }
2693
2694 echo '</div>';
2695 }
2696
2697
2698 public function mxchat_append_to_body_callback() {
2699 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
2700 echo '<label class="toggle-switch">';
2701 printf(
2702 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
2703 esc_attr($append_to_body_checked)
2704 );
2705 echo '<span class="slider"></span>';
2706 echo '</label>';
2707 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>';
2708 }
2709
2710
2711 public function mxchat_privacy_toggle_callback() {
2712 // Check if the privacy toggle is enabled
2713 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
2714
2715 // Retrieve the stored privacy text if it exists
2716 $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>.';
2717
2718 // Output the toggle switch
2719 echo '<label class="toggle-switch">';
2720 printf(
2721 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
2722 esc_attr($privacy_toggle_checked)
2723 );
2724 echo '<span class="slider"></span>';
2725 echo '</label>';
2726 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
2727
2728 // Output the custom text input field
2729 printf(
2730 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
2731 esc_textarea($privacy_text)
2732 );
2733 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
2734 }
2735
2736
2737 public function mxchat_complianz_toggle_callback() {
2738 // Check if the Complianz toggle is enabled
2739 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
2740
2741 // Check if the plugin is activated (paid feature)
2742 $disabled = $this->is_activated ? '' : 'disabled';
2743 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2744
2745 echo '<div class="' . esc_attr($class) . '">';
2746
2747 // Output the toggle switch
2748 echo '<label class="toggle-switch">';
2749 printf(
2750 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
2751 esc_attr($complianz_toggle_checked),
2752 esc_attr($disabled)
2753 );
2754 echo '<span class="slider"></span>';
2755 echo '</label>';
2756 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot.</p>';
2757
2758 // If the feature is not activated, show the Pro feature overlay
2759 if (!$this->is_activated) {
2760 echo '<div class="pro-feature-overlay">';
2761 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2762 echo '</div>';
2763 }
2764
2765 echo '</div>';
2766 }
2767
2768
2769
2770
2771
2772 public function mxchat_link_target_toggle_callback() {
2773 // Check if the toggle is enabled in the options
2774 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
2775
2776 // Output the toggle switch
2777 echo '<label class="toggle-switch">';
2778 printf(
2779 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
2780 esc_attr($link_target_toggle)
2781 );
2782 echo '<span class="slider"></span>';
2783 echo '</label>';
2784 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
2785 }
2786
2787 public function mxchat_chat_persistence_toggle_callback() {
2788 // Check if chat persistence toggle is enabled
2789 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
2790
2791 // Check if the plugin is activated (paid feature)
2792 $disabled = $this->is_activated ? '' : 'disabled';
2793 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2794
2795 echo '<div class="' . esc_attr($class) . '">';
2796
2797 // Output the toggle switch
2798 echo '<label class="toggle-switch">';
2799 printf(
2800 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
2801 esc_attr($chat_persistence_toggle_checked),
2802 esc_attr($disabled)
2803 );
2804 echo '<span class="slider"></span>';
2805 echo '</label>';
2806 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
2807
2808 // If not activated, show Pro feature overlay
2809 if (!$this->is_activated) {
2810 echo '<div class="pro-feature-overlay">';
2811 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2812 echo '</div>';
2813 }
2814
2815 echo '</div>';
2816 }
2817
2818
2819 public function mxchat_popular_question_1_callback() {
2820 printf(
2821 '<input type="text" id="popular_question_1" name="mxchat_options[popular_question_1]" value="%s" placeholder="Enter Popular Question 1" />',
2822 isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
2823 );
2824 echo '<p class="description">This will be the first popular question in the chatbot.</p>';
2825 }
2826
2827 public function mxchat_popular_question_2_callback() {
2828 // Check if the plugin is activated (paid feature)
2829 $disabled = $this->is_activated ? '' : 'disabled';
2830 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2831
2832 echo '<div class="' . esc_attr($class) . '">';
2833
2834 printf(
2835 '<input type="text" id="popular_question_2" name="mxchat_options[popular_question_2]" value="%s" placeholder="Enter Popular Question 2" %s />',
2836 isset($this->options['popular_question_2']) ? esc_attr($this->options['popular_question_2']) : '',
2837 esc_attr($disabled)
2838 );
2839 echo '<p class="description">This will be the second popular question in the chatbot.</p>';
2840
2841 // If not activated, show Pro feature overlay
2842 if (!$this->is_activated) {
2843 echo '<div class="pro-feature-overlay">';
2844 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2845 echo '</div>';
2846 }
2847
2848 echo '</div>';
2849 }
2850
2851 public function mxchat_popular_question_3_callback() {
2852 // Check if the plugin is activated (paid feature)
2853 $disabled = $this->is_activated ? '' : 'disabled';
2854 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2855
2856 echo '<div class="' . esc_attr($class) . '">';
2857
2858 printf(
2859 '<input type="text" id="popular_question_3" name="mxchat_options[popular_question_3]" value="%s" placeholder="Enter Popular Question 3" %s />',
2860 isset($this->options['popular_question_3']) ? esc_attr($this->options['popular_question_3']) : '',
2861 esc_attr($disabled)
2862 );
2863 echo '<p class="description">This will be the third popular question in the chatbot.</p>';
2864
2865 // If not activated, show Pro feature overlay
2866 if (!$this->is_activated) {
2867 echo '<div class="pro-feature-overlay">';
2868 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2869 echo '</div>';
2870 }
2871
2872 echo '</div>';
2873 }
2874
2875 public function mxchat_additional_popular_questions_callback() {
2876 $disabled = $this->is_activated ? '' : 'disabled';
2877 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2878 $additional_questions = isset($this->options['additional_popular_questions']) ? $this->options['additional_popular_questions'] : array();
2879
2880 echo '<div class="' . esc_attr($class) . '">';
2881 echo '<div id="mxchat-additional-questions-container">';
2882
2883 if (!empty($additional_questions)) {
2884 foreach ($additional_questions as $index => $question) {
2885 printf(
2886 '<div class="mxchat-question-row">
2887 <input type="text" name="mxchat_options[additional_popular_questions][]" value="%s" placeholder="Enter Additional Popular Question %d" %s />
2888 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
2889 </div>',
2890 esc_attr($question),
2891 $index + 4, // Start numbering from 4
2892 esc_attr($disabled),
2893 esc_attr($disabled)
2894 );
2895 }
2896 } else {
2897 printf(
2898 '<div class="mxchat-question-row">
2899 <input type="text" name="mxchat_options[additional_popular_questions][]" value="" placeholder="Enter Additional Popular Question 4" %s />
2900 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
2901 </div>',
2902 esc_attr($disabled),
2903 esc_attr($disabled)
2904 );
2905 }
2906
2907 echo '</div>';
2908
2909 printf(
2910 '<button type="button" class="button mxchat-add-question" aria-label="Add question" %s>Add Question</button>',
2911 esc_attr($disabled)
2912 );
2913
2914 echo '<p class="description">You can add more popular questions beyond the default three here.</p>';
2915
2916 if (!$this->is_activated) {
2917 echo '<div class="pro-feature-overlay">';
2918 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2919 echo '</div>';
2920 }
2921
2922 echo '</div>';
2923 }
2924
2925
2926 /**
2927 * Callback for Brave API Key field.
2928 */
2929 public function mxchat_brave_api_key_callback() {
2930 $options = get_option('mxchat_options');
2931 $brave_api_key = isset($options['brave_api_key']) ? esc_attr($options['brave_api_key']) : '';
2932
2933 echo '<div class="api-key-wrapper">';
2934 printf(
2935 '<input type="password" id="brave_api_key" name="mxchat_options[brave_api_key]" value="%s" class="regular-text" />',
2936 $brave_api_key
2937 );
2938 echo '<button type="button" id="toggleBraveApiKeyVisibility" class="button-secondary">Show</button>';
2939 echo '</div>';
2940 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
2941
2942 ?>
2943 <?php
2944 }
2945
2946 /**
2947 * Callback for Number of Images to Return field.
2948 */
2949 public function mxchat_brave_image_count_callback() {
2950 $options = get_option('mxchat_options');
2951 $brave_image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
2952
2953 printf(
2954 '<input type="number" id="brave_image_count" name="mxchat_options[brave_image_count]" value="%d" min="1" max="6" />',
2955 $brave_image_count
2956 );
2957 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
2958 }
2959
2960 /**
2961 * Callback for Safe Search field.
2962 */
2963 public function mxchat_brave_safe_search_callback() {
2964 $options = get_option('mxchat_options');
2965 $brave_safe_search = isset($options['brave_safe_search']) ? esc_attr($options['brave_safe_search']) : 'strict';
2966
2967 ?>
2968 <select id="brave_safe_search" name="mxchat_options[brave_safe_search]">
2969 <option value="strict" <?php selected($brave_safe_search, 'strict'); ?>><?php _e('Strict', 'mxchat'); ?></option>
2970 <option value="off" <?php selected($brave_safe_search, 'off'); ?>><?php _e('Off', 'mxchat'); ?></option>
2971 </select>
2972 <p class="description"><?php _e('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat'); ?></p>
2973 <?php
2974 }
2975
2976
2977 /**
2978 * Callback for Number of News Articles field.
2979 */
2980 public function mxchat_brave_news_count_callback() {
2981 $options = get_option('mxchat_options');
2982 $brave_news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
2983
2984 printf(
2985 '<input type="number" id="brave_news_count" name="mxchat_options[brave_news_count]" value="%d" min="1" max="10" />',
2986 $brave_news_count
2987 );
2988 echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
2989 }
2990
2991 /**
2992 * Callback for Country field.
2993 */
2994 public function mxchat_brave_country_callback() {
2995 $options = get_option('mxchat_options');
2996 $brave_country = isset($options['brave_country']) ? esc_attr($options['brave_country']) : 'us';
2997
2998 printf(
2999 '<input type="text" id="brave_country" name="mxchat_options[brave_country]" value="%s" maxlength="2" class="small-text" />',
3000 $brave_country
3001 );
3002 echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
3003 }
3004
3005 /**
3006 * Callback for Language field.
3007 */
3008 public function mxchat_brave_language_callback() {
3009 $options = get_option('mxchat_options');
3010 $brave_language = isset($options['brave_language']) ? esc_attr($options['brave_language']) : 'en';
3011
3012 printf(
3013 '<input type="text" id="brave_language" name="mxchat_options[brave_language]" value="%s" maxlength="2" class="small-text" />',
3014 $brave_language
3015 );
3016 echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
3017 }
3018
3019
3020
3021
3022
3023 // Section Callback
3024 public function mxchat_pdf_intent_section_callback() {
3025 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
3026 }
3027
3028 public function mxchat_chat_toolbar_toggle_callback() {
3029 $checked = isset($this->options['chat_toolbar_toggle']) && $this->options['chat_toolbar_toggle'] === 'on' ? 'checked' : '';
3030 $disabled = $this->is_activated ? '' : 'disabled';
3031 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3032
3033 echo '<div class="' . esc_attr($class) . '">';
3034 echo '<label class="toggle-switch">';
3035 printf(
3036 '<input type="checkbox" id="chat_toolbar_toggle" name="mxchat_options[chat_toolbar_toggle]" value="on" %s %s>',
3037 esc_attr($checked),
3038 esc_attr($disabled)
3039 );
3040 echo '<span class="slider"></span>';
3041 echo '</label>';
3042
3043 if (!$this->is_activated) {
3044 echo '<div class="pro-feature-overlay">';
3045 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3046 echo '</div>';
3047 }
3048
3049 echo '</div>';
3050 echo '<p class="description">Enable to display the chat toolbar (default is hidden).</p>';
3051 }
3052
3053
3054
3055 // Intent Trigger Text Callback
3056 public function mxchat_pdf_intent_trigger_text_callback() {
3057 $disabled = $this->is_activated ? '' : 'disabled';
3058 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3059
3060 echo '<div class="' . esc_attr($class) . '">';
3061 printf(
3062 '<textarea id="pdf_intent_trigger_text" name="mxchat_options[pdf_intent_trigger_text]" rows="3" cols="50" placeholder="Enter trigger text" %s>%s</textarea>',
3063 esc_attr($disabled),
3064 isset($this->options['pdf_intent_trigger_text']) ? esc_textarea($this->options['pdf_intent_trigger_text']) : ''
3065 );
3066 echo '<p class="description">Text displayed when the intent is triggered.</p>';
3067
3068 if (!$this->is_activated) {
3069 echo '<div class="pro-feature-overlay">';
3070 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3071 echo '</div>';
3072 }
3073 echo '</div>';
3074 }
3075
3076 // Success Text Callback
3077 public function mxchat_pdf_intent_success_text_callback() {
3078 $disabled = $this->is_activated ? '' : 'disabled';
3079 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3080
3081 echo '<div class="' . esc_attr($class) . '">';
3082 printf(
3083 '<textarea id="pdf_intent_success_text" name="mxchat_options[pdf_intent_success_text]" rows="3" cols="50" placeholder="Enter success text" %s>%s</textarea>',
3084 esc_attr($disabled),
3085 isset($this->options['pdf_intent_success_text']) ? esc_textarea($this->options['pdf_intent_success_text']) : ''
3086 );
3087 echo '<p class="description">Text displayed when the intent is successful.</p>';
3088
3089 if (!$this->is_activated) {
3090 echo '<div class="pro-feature-overlay">';
3091 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3092 echo '</div>';
3093 }
3094 echo '</div>';
3095 }
3096
3097 // Error Text Callback
3098 public function mxchat_pdf_intent_error_text_callback() {
3099 $disabled = $this->is_activated ? '' : 'disabled';
3100 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3101
3102 echo '<div class="' . esc_attr($class) . '">';
3103 printf(
3104 '<textarea id="pdf_intent_error_text" name="mxchat_options[pdf_intent_error_text]" rows="3" cols="50" placeholder="Enter error text" %s>%s</textarea>',
3105 esc_attr($disabled),
3106 isset($this->options['pdf_intent_error_text']) ? esc_textarea($this->options['pdf_intent_error_text']) : ''
3107 );
3108 echo '<p class="description">Text displayed when an error occurs during the intent.</p>';
3109
3110 if (!$this->is_activated) {
3111 echo '<div class="pro-feature-overlay">';
3112 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3113 echo '</div>';
3114 }
3115 echo '</div>';
3116 }
3117
3118 // Maximum PDF Pages Callback
3119 public function mxchat_pdf_max_pages_callback() {
3120 $disabled = $this->is_activated ? '' : 'disabled';
3121 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3122 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
3123
3124 echo '<div class="' . esc_attr($class) . '">';
3125 printf(
3126 '<input type="range" id="pdf_max_pages" name="mxchat_options[pdf_max_pages]" min="1" max="69" value="%d" %s oninput="document.getElementById(\'pdf_max_pages_output\').innerText = this.value;" />',
3127 esc_attr($max_pages),
3128 esc_attr($disabled)
3129 );
3130 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
3131 echo '<p class="description">Set the maximum number of PDF pages users can upload for processing. (1-69 pages)</p>';
3132
3133 if (!$this->is_activated) {
3134 echo '<div class="pro-feature-overlay">';
3135 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3136 echo '</div>';
3137 }
3138 echo '</div>';
3139 }
3140
3141
3142 // Status Toggle Callback
3143 public function mxchat_live_agent_status_callback() {
3144 $disabled = $this->is_activated ? '' : 'disabled';
3145 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3146 $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'offline';
3147
3148 echo '<div class="' . esc_attr($class) . '">';
3149 echo '<label class="mxchat-switch">';
3150 printf(
3151 '<input type="checkbox" id="live_agent_status" name="mxchat_options[live_agent_status]" value="online" %s %s />',
3152 checked($status, 'online', false),
3153 esc_attr($disabled)
3154 );
3155 echo '<span class="mxchat-slider"></span>';
3156 echo '</label>';
3157 echo '<label for="live_agent_status" class="mxchat-status-label">';
3158 echo '<span class="status-text">' . ($status === 'online' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
3159 echo '</label>';
3160
3161 if (!$this->is_activated) {
3162 echo '<div class="pro-feature-overlay">';
3163 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3164 echo '</div>';
3165 }
3166 echo '</div>';
3167 }
3168
3169 // Away Message Callback
3170 public function mxchat_live_agent_away_message_callback() {
3171 $disabled = $this->is_activated ? '' : 'disabled';
3172 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3173 $message = isset($this->options['live_agent_away_message'])
3174 ? $this->options['live_agent_away_message']
3175 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
3176
3177 echo '<div class="' . esc_attr($class) . '">';
3178 printf(
3179 '<textarea id="live_agent_away_message" name="mxchat_options[live_agent_away_message]" rows="3" cols="50" %s>%s</textarea>',
3180 esc_attr($disabled),
3181 esc_textarea($message)
3182 );
3183 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
3184
3185 if (!$this->is_activated) {
3186 echo '<div class="pro-feature-overlay">';
3187 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3188 echo '</div>';
3189 }
3190 echo '</div>';
3191 }
3192 public function mxchat_live_agent_notification_message_callback() {
3193 $disabled = $this->is_activated ? '' : 'disabled';
3194 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3195 $message = isset($this->options['live_agent_notification_message'])
3196 ? $this->options['live_agent_notification_message']
3197 : __('Live agent has been notified.', 'mxchat');
3198
3199 echo '<div class="' . esc_attr($class) . '">';
3200 printf(
3201 '<textarea id="live_agent_notification_message" name="mxchat_options[live_agent_notification_message]" rows="3" cols="50" %s>%s</textarea>',
3202 esc_attr($disabled),
3203 esc_textarea($message)
3204 );
3205 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
3206
3207 if (!$this->is_activated) {
3208 echo '<div class="pro-feature-overlay">';
3209 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3210 echo '</div>';
3211 }
3212 echo '</div>';
3213 }
3214
3215 public function mxchat_live_agent_webhook_url_callback() {
3216 $disabled = $this->is_activated ? '' : 'disabled';
3217 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3218 $webhook_url = isset($this->options['live_agent_webhook_url'])
3219 ? esc_url($this->options['live_agent_webhook_url'])
3220 : esc_url(get_option('live_agent_webhook_url', ''));
3221
3222 echo '<div class="' . esc_attr($class) . '">';
3223 printf(
3224 '<input type="password" id="live_agent_webhook_url" name="mxchat_options[live_agent_webhook_url]" value="%s" class="regular-text" %s />',
3225 $webhook_url,
3226 esc_attr($disabled)
3227 );
3228 echo '<button type="button" id="toggleWebhookUrlVisibility">Show</button>';
3229 echo '<p class="description">Enter your Slack webhook URL for live agent notifications.</p>';
3230 if (!$this->is_activated) {
3231 echo '<div class="pro-feature-overlay">';
3232 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3233 echo '</div>';
3234 }
3235 echo '</div>';
3236 }
3237
3238 public function mxchat_live_agent_secret_key_callback() {
3239 $disabled = $this->is_activated ? '' : 'disabled';
3240 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3241
3242 echo '<div class="' . esc_attr($class) . '">';
3243 printf(
3244 '<input type="password" id="live_agent_secret_key" name="mxchat_options[live_agent_secret_key]" value="%s" class="regular-text" %s />',
3245 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
3246 esc_attr($disabled)
3247 );
3248 echo '<button type="button" id="toggleSecretKeyVisibility">Show</button>';
3249 echo '<p class="description">Secret key for validating Slack requests. Keep this secure.</p>';
3250
3251 if (!$this->is_activated) {
3252 echo '<div class="pro-feature-overlay">';
3253 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3254 echo '</div>';
3255 }
3256 echo '</div>';
3257 }
3258
3259 public function mxchat_live_agent_bot_token_callback() {
3260 $disabled = $this->is_activated ? '' : 'disabled';
3261 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3262
3263 echo '<div class="' . esc_attr($class) . '">';
3264 printf(
3265 '<input type="password" id="live_agent_bot_token" name="mxchat_options[live_agent_bot_token]" value="%s" class="regular-text" %s />',
3266 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
3267 esc_attr($disabled)
3268 );
3269 echo '<button type="button" id="toggleBotTokenVisibility">Show</button>';
3270 echo '<p class="description">Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.</p>';
3271
3272 if (!$this->is_activated) {
3273 echo '<div class="pro-feature-overlay">';
3274 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3275 echo '</div>';
3276 }
3277 echo '</div>';
3278 }
3279
3280 public function mxchat_enqueue_admin_assets() {
3281 wp_enqueue_style('wp-color-picker');
3282
3283 // Get the plugin version or file modification time for cache busting
3284 $plugin_version = '1.5.1'; // Replace this with your plugin's version
3285
3286 // File paths
3287 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
3288 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
3289 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
3290 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
3291
3292 // Check if files exist and get modification times
3293 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
3294 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
3295 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
3296 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
3297
3298 // Enqueue scripts and styles with corrected paths
3299 wp_enqueue_script(
3300 'mxchat-color-picker',
3301 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
3302 array('wp-color-picker'),
3303 $color_picker_version,
3304 true
3305 );
3306
3307 wp_enqueue_script(
3308 'mxchat-embedding-check',
3309 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
3310 array(),
3311 $embedding_check_version,
3312 true
3313 );
3314
3315 wp_enqueue_script(
3316 'mxchat-transcripts-js',
3317 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
3318 array('jquery'),
3319 $transcripts_js_version,
3320 true
3321 );
3322
3323 wp_enqueue_script(
3324 'mxchat-admin-js',
3325 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
3326 array('jquery'),
3327 $plugin_version,
3328 true
3329 );
3330
3331
3332
3333 // Localize the script with data for the activation process
3334 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
3335 'ajax_url' => admin_url('admin-ajax.php'),
3336 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
3337 ));
3338
3339 wp_enqueue_style(
3340 'mxchat-admin-css',
3341 plugin_dir_url(__FILE__) . '../css/admin-style.css',
3342 array(),
3343 $admin_css_version
3344 );
3345
3346 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
3347 'ajax_url' => admin_url('admin-ajax.php'),
3348 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
3349 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
3350 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
3351 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
3352 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
3353 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
3354 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
3355 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
3356 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
3357 'icon_color' => $this->options['icon_color'] ?? '#fff',
3358 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
3359 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
3360 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
3361
3362 // New fields for Loops Integration
3363 'loops_api_key' => $this->options['loops_api_key'] ?? '',
3364 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
3365 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
3366 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.',
3367 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.",
3368 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?",
3369 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
3370 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
3371
3372 // Live Agent Integration
3373 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
3374 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
3375 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
3376
3377 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
3378 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
3379 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
3380 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
3381 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
3382 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
3383
3384 ));
3385
3386 // Localize the script to pass the nonce and other data to JavaScript
3387 wp_localize_script('mxchat-admin-js', 'mxchatInlineEdit', array(
3388 'nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
3389 'ajax_url' => admin_url('admin-ajax.php')
3390 ));
3391
3392
3393 }
3394
3395 public function mxchat_sanitize($input) {
3396 $new_input = array();
3397
3398 if (isset($input['api_key'])) {
3399 $new_input['api_key'] = sanitize_text_field($input['api_key']);
3400 }
3401
3402 if (isset($input['xai_api_key'])) {
3403 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
3404 }
3405
3406 if (isset($input['claude_api_key'])) {
3407 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
3408 }
3409
3410 if (isset($input['enable_woocommerce_integration'])) {
3411 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
3412
3413 }
3414
3415 if (isset($input['privacy_toggle'])) {
3416 $new_input['privacy_toggle'] = $input['privacy_toggle'];
3417 }
3418
3419 if (isset($input['complianz_toggle'])) {
3420 $new_input['complianz_toggle'] = $input['complianz_toggle'];
3421 }
3422
3423 // Handle custom privacy text input
3424 if (isset($input['privacy_text'])) {
3425 // Allow basic HTML for links
3426 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
3427 }
3428
3429 if (isset($input['system_prompt_instructions'])) {
3430 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
3431 }
3432
3433 if (isset($input['mxchat_pro_email'])) {
3434 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
3435 }
3436
3437 if (isset($input['mxchat_activation_key'])) {
3438 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
3439 }
3440
3441 if (isset($input['append_to_body'])) {
3442 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
3443 }
3444
3445 if (isset($input['top_bar_title'])) {
3446 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
3447 }
3448
3449 if (isset($input['intro_message'])) {
3450 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
3451 }
3452
3453 if (isset($input['input_copy'])) {
3454 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
3455 }
3456
3457
3458 if (isset($input['rate_limit_message'])) {
3459 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
3460 }
3461
3462 if (isset($input['rate_limit_logged_in'])) {
3463 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3464 $rate_limit = sanitize_text_field($input['rate_limit_logged_in']);
3465
3466 if (in_array($rate_limit, $allowed_limits, true)) {
3467 $new_input['rate_limit_logged_in'] = $rate_limit;
3468 } else {
3469 $new_input['rate_limit_logged_in'] = '100'; // Default for logged-in users
3470 }
3471 }
3472
3473 if (isset($input['rate_limit_logged_out'])) {
3474 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3475 $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
3476
3477 if (in_array($rate_limit, $allowed_limits, true)) {
3478 $new_input['rate_limit_logged_out'] = $rate_limit;
3479 } else {
3480 $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
3481 }
3482 }
3483
3484 if (isset($input['pre_chat_message'])) {
3485 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
3486 }
3487
3488 if (isset($input['model'])) {
3489 $allowed_models = array(
3490 'grok-beta',
3491 'claude-3-5-sonnet-20241022',
3492 'claude-3-opus-20240229',
3493 'claude-3-sonnet-20240229',
3494 'claude-3-haiku-20240307',
3495 'gpt-4o',
3496 'gpt-4o-mini',
3497 'gpt-4-turbo',
3498 'gpt-4',
3499 'gpt-3.5-turbo',
3500 );
3501 if (in_array($input['model'], $allowed_models)) {
3502 $new_input['model'] = sanitize_text_field($input['model']);
3503 }
3504 }
3505
3506 // Sanitize new pro features
3507 if (isset($input['close_button_color'])) {
3508 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
3509 }
3510
3511 if (isset($input['chatbot_bg_color'])) {
3512 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
3513 }
3514
3515 if (isset($input['woocommerce_consumer_key'])) {
3516 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
3517 }
3518
3519 if (isset($input['woocommerce_consumer_secret'])) {
3520 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
3521 }
3522
3523 if (isset($input['user_message_bg_color'])) {
3524 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
3525 }
3526
3527 if (isset($input['user_message_font_color'])) {
3528 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
3529 }
3530
3531 if (isset($input['bot_message_bg_color'])) {
3532 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
3533 }
3534
3535 if (isset($input['bot_message_font_color'])) {
3536 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
3537 }
3538
3539 if (isset($input['live_agent_message_bg_color'])) {
3540 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
3541 }
3542
3543 if (isset($input['live_agent_message_font_color'])) {
3544 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
3545 }
3546
3547 if (isset($input['mode_indicator_bg_color'])) {
3548 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
3549 }
3550
3551 if (isset($input['mode_indicator_font_color'])) {
3552 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
3553 }
3554
3555 if (isset($input['toolbar_icon_color'])) {
3556 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
3557 }
3558
3559 if (isset($input['top_bar_bg_color'])) {
3560 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
3561 }
3562
3563 if (isset($input['send_button_font_color'])) {
3564 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
3565 }
3566
3567 if (isset($input['chatbot_background_color'])) {
3568 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
3569 }
3570
3571 if (isset($input['icon_color'])) {
3572 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
3573 }
3574
3575 if (isset($input['custom_icon'])) {
3576 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
3577 }
3578
3579 if (isset($input['chat_input_font_color'])) {
3580 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
3581 }
3582
3583 // Sanitize link_target_toggle
3584 if (isset($input['link_target_toggle'])) {
3585 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
3586 }
3587
3588 // Sanitize Loops API Key
3589 if (isset($input['loops_api_key'])) {
3590 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
3591 }
3592
3593 if (isset($input['chat_persistence_toggle'])) {
3594 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
3595 }
3596
3597
3598
3599 if (isset($input['popular_question_1'])) {
3600 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
3601 }
3602
3603 if (isset($input['popular_question_2'])) {
3604 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
3605 }
3606
3607 if (isset($input['popular_question_3'])) {
3608 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
3609 }
3610
3611 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
3612 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
3613 }
3614
3615 // Sanitize Loops Mailing List
3616 if (isset($input['loops_mailing_list'])) {
3617 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
3618 }
3619
3620 // Sanitize Triggered Phrase Response
3621 if (isset($input['triggered_phrase_response'])) {
3622 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
3623 }
3624
3625 if (isset($input['email_capture_response'])) {
3626 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
3627 }
3628
3629
3630 // Sanitize Brave Search Settings
3631 if (isset($input['brave_api_key'])) {
3632 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
3633 }
3634
3635 if (isset($input['brave_image_count'])) {
3636 $image_count = intval($input['brave_image_count']);
3637 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
3638 }
3639
3640 if (isset($input['brave_safe_search'])) {
3641 $allowed = array('strict', 'off');
3642 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
3643 }
3644
3645 if (isset($input['brave_news_count'])) {
3646 $news_count = intval($input['brave_news_count']);
3647 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
3648 }
3649
3650 if (isset($input['brave_country'])) {
3651 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
3652 }
3653
3654 if (isset($input['brave_language'])) {
3655 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
3656 }
3657
3658
3659 if (isset($input['chat_toolbar_toggle'])) {
3660 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
3661 }
3662
3663
3664 if (isset($input['pdf_intent_trigger_text'])) {
3665 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
3666 }
3667
3668 if (isset($input['pdf_intent_success_text'])) {
3669 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
3670 }
3671
3672 if (isset($input['pdf_intent_error_text'])) {
3673 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
3674 }
3675
3676 if (isset($input['pdf_max_pages'])) {
3677 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
3678 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
3679 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
3680 }
3681 }
3682
3683
3684 if (isset($input['live_agent_webhook_url'])) {
3685 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
3686 }
3687 if (isset($input['live_agent_secret_key'])) {
3688 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
3689 }
3690
3691 // Live Agent Integration
3692 if (isset($input['live_agent_bot_token'])) {
3693 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
3694 }
3695
3696 if (isset($input['live_agent_status'])) {
3697 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'online') ? 'online' : 'offline';
3698 }
3699 if (isset($input['live_agent_away_message'])) {
3700 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
3701 }
3702 if (isset($input['live_agent_notification_message'])) {
3703 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
3704 }
3705
3706
3707 return $new_input;
3708 }
3709
3710
3711 // Method to append the chatbot to the body
3712 public function mxchat_append_chatbot_to_body() {
3713 $options = get_option('mxchat_options');
3714 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
3715 echo do_shortcode('[mxchat_chatbot floating="yes"]');
3716 }
3717 }
3718
3719
3720
3721 private static function mxchat_extract_main_content($html) {
3722 $dom = new DOMDocument;
3723 libxml_use_internal_errors(true); // Suppress HTML parsing errors
3724 @$dom->loadHTML($html);
3725 libxml_clear_errors();
3726
3727 $xpath = new DOMXPath($dom);
3728
3729 // Simplified selectors focusing on common content areas
3730 $selectors = [
3731 '//article',
3732 '//*[@id="content"]',
3733 '//*[@class="entry-content"]',
3734 '//main',
3735 ];
3736
3737 foreach ($selectors as $selector) {
3738 $nodes = $xpath->query($selector);
3739 if ($nodes->length > 0) {
3740 $content = '';
3741 foreach ($nodes as $node) {
3742 $content .= $dom->saveHTML($node);
3743 }
3744 return $content;
3745 }
3746 }
3747
3748 // Fallback: Return the entire body content if no specific selector matches
3749 $body = $dom->getElementsByTagName('body');
3750 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
3751 }
3752
3753 public function mxchat_handle_sitemap_submission() {
3754 // Check if the form was submitted and verify permissions
3755 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
3756 wp_die(esc_html__('Unauthorized access', 'mxchat'));
3757 }
3758
3759 // Verify nonce
3760 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
3761
3762 // Validate URL
3763 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
3764 set_transient('mxchat_admin_notice_error',
3765 esc_html__('Please provide a valid URL.', 'mxchat'),
3766 30
3767 );
3768 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3769 exit;
3770 }
3771
3772 $submitted_url = esc_url_raw($_POST['sitemap_url']);
3773 $response = wp_remote_get($submitted_url, array('timeout' => 30));
3774
3775 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
3776 $error_message = is_wp_error($response) ? $response->get_error_message() : 'Failed to fetch URL';
3777 set_transient('mxchat_admin_notice_error',
3778 sprintf(
3779 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
3780 esc_html($error_message)
3781 ),
3782 30
3783 );
3784 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3785 exit;
3786 }
3787
3788 $content_type = wp_remote_retrieve_header($response, 'content-type');
3789 $body_content = wp_remote_retrieve_body($response);
3790
3791 if (empty($body_content)) {
3792 set_transient('mxchat_admin_notice_error',
3793 esc_html__('Empty response received from URL.', 'mxchat'),
3794 30
3795 );
3796 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3797 exit;
3798 }
3799
3800 // Handle PDF URL
3801 if ($this->is_pdf_url($submitted_url, $response)) {
3802 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
3803
3804 if ($result === 'scheduled') {
3805 set_transient(
3806 'mxchat_last_pdf_url',
3807 sanitize_text_field($submitted_url),
3808 DAY_IN_SECONDS
3809 );
3810 set_transient('mxchat_admin_notice_info',
3811 esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
3812 30
3813 );
3814 } else {
3815 set_transient('mxchat_admin_notice_error',
3816 esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
3817 30
3818 );
3819 }
3820
3821 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3822 exit;
3823 }
3824
3825 // Handle Sitemap XML
3826 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
3827 libxml_use_internal_errors(true);
3828 $xml = simplexml_load_string($body_content);
3829 $xml_errors = libxml_get_errors();
3830 libxml_clear_errors();
3831
3832 if ($xml === false || !empty($xml_errors)) {
3833 set_transient('mxchat_admin_notice_error',
3834 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
3835 30
3836 );
3837 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3838 exit;
3839 }
3840
3841 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
3842
3843 if ($result === 'scheduled') {
3844 set_transient(
3845 'mxchat_last_sitemap_url',
3846 sanitize_text_field($submitted_url),
3847 DAY_IN_SECONDS
3848 );
3849 set_transient('mxchat_admin_notice_info',
3850 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
3851 30
3852 );
3853 } else {
3854 set_transient('mxchat_admin_notice_error',
3855 esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
3856 30
3857 );
3858 }
3859
3860 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3861 exit;
3862 }
3863
3864 // Handle Regular URL
3865 $page_content = $this->mxchat_extract_main_content($body_content);
3866 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
3867
3868 if (empty($sanitized_content)) {
3869 set_transient('mxchat_admin_notice_error',
3870 esc_html__('No valid content found on the provided URL.', 'mxchat'),
3871 30
3872 );
3873 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3874 exit;
3875 }
3876
3877 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
3878 if (is_array($embedding_vector)) {
3879 MxChat_Utils::submit_content_to_db(
3880 $sanitized_content,
3881 $submitted_url,
3882 $this->options['api_key']
3883 );
3884 set_transient('mxchat_admin_notice_success',
3885 esc_html__('URL content successfully submitted!', 'mxchat'),
3886 30
3887 );
3888 } else {
3889 set_transient('mxchat_admin_notice_error',
3890 esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
3891 30
3892 );
3893 }
3894
3895 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3896 exit;
3897 }
3898 private function is_pdf_url($url, $response) {
3899 $content_type = wp_remote_retrieve_header($response, 'content-type');
3900 $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
3901
3902 return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
3903 }
3904
3905 private function handle_pdf_for_knowledge_base($pdf_url, $response) {
3906 if (!current_user_can('manage_options')) {
3907 //error_log('Unauthorized PDF processing attempt');
3908 return false;
3909 }
3910
3911 $pdf_url = esc_url_raw($pdf_url);
3912 $upload_dir = wp_upload_dir();
3913
3914 if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
3915 //error_log(sprintf('Upload directory error: %s', esc_html($upload_dir['error'])));
3916 return false;
3917 }
3918
3919 $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
3920 $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
3921
3922 $response_body = wp_remote_retrieve_body($response);
3923 if (empty($response_body)) {
3924 //error_log('Empty PDF response body');
3925 return false;
3926 }
3927
3928 if (!wp_mkdir_p(dirname($pdf_path))) {
3929 //error_log(sprintf('Failed to create directory for PDF: %s', esc_html($pdf_path)));
3930 return false;
3931 }
3932
3933 try {
3934 file_put_contents($pdf_path, $response_body);
3935
3936 if (!file_exists($pdf_path)) {
3937 throw new Exception('Failed to save PDF file');
3938 }
3939
3940 $parser = new \Smalot\PdfParser\Parser();
3941 $pdf = $parser->parseFile($pdf_path);
3942 $total_pages = absint(count($pdf->getPages()));
3943
3944 if ($total_pages < 1) {
3945 throw new Exception('Invalid PDF: no pages found');
3946 }
3947
3948 wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
3949 'pdf_path' => $pdf_path,
3950 'pdf_url' => $pdf_url,
3951 'total_pages' => $total_pages,
3952 'batch_size' => absint(15),
3953 'batch_pause' => absint(10)
3954 ));
3955
3956 $status_data = array(
3957 'total_pages' => $total_pages,
3958 'processed_pages' => 0,
3959 'status' => 'processing',
3960 'last_update' => time()
3961 );
3962
3963 set_transient(
3964 sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
3965 array_map('sanitize_text_field', $status_data),
3966 DAY_IN_SECONDS
3967 );
3968
3969 return 'scheduled';
3970
3971 } catch (Exception $e) {
3972 //error_log(sprintf('Error preparing PDF for processing: %s', esc_html($e->getMessage())));
3973 if (file_exists($pdf_path)) {
3974 wp_delete_file($pdf_path);
3975 }
3976 return false;
3977 }
3978 }
3979 public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
3980 // Validate inputs
3981 $pdf_path = sanitize_text_field($pdf_path);
3982 $pdf_url = esc_url_raw($pdf_url);
3983 $total_pages = absint($total_pages);
3984 $batch_size = absint($batch_size);
3985 $batch_pause = absint($batch_pause);
3986
3987 try {
3988 if (!file_exists($pdf_path)) {
3989 throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
3990 }
3991
3992 $parser = new \Smalot\PdfParser\Parser();
3993 $pdf = $parser->parseFile($pdf_path);
3994 $pages = $pdf->getPages();
3995
3996 // Get current progress
3997 $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
3998 $status = get_transient($status_key);
3999
4000 if (!$status || !is_array($status)) {
4001 throw new Exception('Invalid status data retrieved from transient');
4002 }
4003
4004 $start_page = absint($status['processed_pages']);
4005 $end_page = min($start_page + $batch_size, $total_pages);
4006
4007 $instance = new self(); // Create an instance of the class
4008
4009 for ($i = $start_page; $i < $end_page; $i++) {
4010 $text = $pages[$i]->getText();
4011 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
4012
4013 if (!empty($sanitized_content)) {
4014 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4015 if (is_array($embedding_vector)) {
4016 $metadata = array(
4017 'document_type' => 'pdf',
4018 'total_pages' => $total_pages,
4019 'current_page' => $i + 1,
4020 'prev_page' => $i > 0 ? $i : null,
4021 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
4022 'source_url' => $pdf_url
4023 );
4024
4025 $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
4026 $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
4027
4028 $options = get_option('mxchat_options');
4029 MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
4030 }
4031 }
4032
4033 // Update progress with sanitized data
4034 $status['processed_pages'] = absint($i + 1);
4035 $status['last_update'] = time();
4036 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4037 }
4038
4039 // Schedule next batch if needed
4040 if ($end_page < $total_pages) {
4041 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
4042 'pdf_path' => $pdf_path,
4043 'pdf_url' => $pdf_url,
4044 'total_pages' => $total_pages,
4045 'batch_size' => $batch_size,
4046 'batch_pause' => $batch_pause
4047 ));
4048 } else {
4049 // Processing complete
4050 $status['status'] = 'complete';
4051 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4052 if (file_exists($pdf_path)) {
4053 wp_delete_file($pdf_path);
4054 }
4055 }
4056
4057 } catch (\Exception $e) {
4058 $status['status'] = 'error';
4059 $status['error'] = sanitize_text_field($e->getMessage());
4060 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4061 if (file_exists($pdf_path)) {
4062 wp_delete_file($pdf_path);
4063 }
4064 }
4065 }
4066
4067 public function get_pdf_processing_status($pdf_url) {
4068 $pdf_url = esc_url_raw($pdf_url);
4069 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
4070
4071 if (!$status || !is_array($status)) {
4072 return false;
4073 }
4074
4075 return array(
4076 'total_pages' => absint($status['total_pages']),
4077 'processed_pages' => absint($status['processed_pages']),
4078 'percentage' => round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100),
4079 'status' => sanitize_text_field($status['status']),
4080 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4081 );
4082 }
4083
4084 private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
4085 if (!current_user_can('manage_options')) {
4086 //error_log('Unauthorized sitemap processing attempt');
4087 return false;
4088 }
4089
4090 try {
4091 $sitemap_url = esc_url_raw($sitemap_url);
4092
4093 if (!$xml || !is_object($xml)) {
4094 throw new Exception('Invalid XML object provided');
4095 }
4096
4097 $urls = [];
4098 foreach ($xml->url as $url_element) {
4099 $url = esc_url_raw((string)$url_element->loc);
4100 if ($url) {
4101 $urls[] = $url;
4102 }
4103 }
4104
4105 $total_urls = absint(count($urls));
4106
4107 if ($total_urls < 1) {
4108 throw new Exception('No valid URLs found in sitemap');
4109 }
4110
4111 wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
4112 'urls' => $urls,
4113 'sitemap_url' => $sitemap_url,
4114 'total_urls' => $total_urls,
4115 'batch_size' => absint(10),
4116 'batch_pause' => absint(5)
4117 ));
4118
4119 $status_data = array(
4120 'total_urls' => $total_urls,
4121 'processed_urls' => 0,
4122 'status' => 'processing',
4123 'last_update' => time()
4124 );
4125
4126 set_transient(
4127 sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
4128 array_map('sanitize_text_field', $status_data),
4129 DAY_IN_SECONDS
4130 );
4131
4132 return 'scheduled';
4133
4134 } catch (\Exception $e) {
4135 //error_log(sprintf('Error preparing sitemap for processing: %s', esc_html($e->getMessage())));
4136 return false;
4137 }
4138 }
4139 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
4140 // Validate inputs
4141 $sitemap_url = esc_url_raw($sitemap_url);
4142 $total_urls = absint($total_urls);
4143 $batch_size = absint($batch_size);
4144 $batch_pause = absint($batch_pause);
4145
4146 if (!is_array($urls) || empty($urls)) {
4147 return;
4148 }
4149
4150 try {
4151 $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
4152 $status = get_transient($status_key);
4153
4154 if (!$status || !is_array($status)) {
4155 throw new Exception('Invalid status data retrieved from transient');
4156 }
4157
4158 $start_url = absint($status['processed_urls']);
4159 $end_url = min($start_url + $batch_size, $total_urls);
4160
4161 $instance = new self(); // Create an instance of the class
4162 $embedding_success = true;
4163
4164 for ($i = $start_url; $i < $end_url; $i++) {
4165 $page_url = esc_url_raw($urls[$i]);
4166 $page_response = wp_remote_get($page_url);
4167
4168 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
4169 continue;
4170 }
4171
4172 $page_html = wp_remote_retrieve_body($page_response);
4173 $page_content = $instance->mxchat_extract_main_content($page_html); // Call via instance
4174 $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content); // Call via instance
4175
4176 if (!empty($sanitized_content)) {
4177 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4178 if (is_array($embedding_vector)) {
4179 $options = get_option('mxchat_options');
4180 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
4181 } else {
4182 $embedding_success = false;
4183 }
4184 }
4185
4186 $status['processed_urls'] = absint($i + 1);
4187 $status['last_update'] = time();
4188 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4189 }
4190
4191 if ($end_url < $total_urls) {
4192 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
4193 'urls' => array_map('esc_url_raw', $urls),
4194 'sitemap_url' => $sitemap_url,
4195 'total_urls' => $total_urls,
4196 'batch_size' => $batch_size,
4197 'batch_pause' => $batch_pause,
4198 ));
4199 } else {
4200 $status['status'] = 'complete';
4201 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4202 }
4203 } catch (\Exception $e) {
4204 $status['status'] = 'error';
4205 $status['error'] = sanitize_text_field($e->getMessage());
4206 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4207 }
4208 }
4209
4210 public function get_sitemap_processing_status($sitemap_url) {
4211 $sitemap_url = esc_url_raw($sitemap_url);
4212 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
4213
4214 if (!$status || !is_array($status)) {
4215 return false;
4216 }
4217
4218 return array(
4219 'total_urls' => absint($status['total_urls']),
4220 'processed_urls' => absint($status['processed_urls']),
4221 'percentage' => round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100),
4222 'status' => sanitize_text_field($status['status']),
4223 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4224 );
4225 }
4226 public function mxchat_stop_processing() {
4227 // Verify permissions
4228 if (!current_user_can('manage_options')) {
4229 wp_die(esc_html__('Unauthorized access', 'mxchat'));
4230 }
4231
4232 // Verify nonce
4233 check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
4234
4235 // Get the last sitemap URL and clear its transient
4236 $sitemap_url = get_transient('mxchat_last_sitemap_url');
4237 if ($sitemap_url) {
4238 delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
4239 delete_transient('mxchat_last_sitemap_url');
4240 }
4241
4242 // Get the last PDF URL and clear its transient
4243 $pdf_url = get_transient('mxchat_last_pdf_url');
4244 if ($pdf_url) {
4245 delete_transient('mxchat_pdf_status_' . md5($pdf_url));
4246 delete_transient('mxchat_last_pdf_url');
4247 }
4248
4249 // Unschedule any pending sitemap events
4250 $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
4251 if ($timestamp) {
4252 wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
4253 }
4254
4255 // Redirect back with a success message
4256 set_transient('mxchat_admin_notice_success',
4257 esc_html__('Processing has been stopped successfully.', 'mxchat'),
4258 30
4259 );
4260 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
4261 exit;
4262 }
4263 private function mxchat_sanitize_content_for_api($content) {
4264 // Remove script, style tags, and HTML comments
4265 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
4266 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
4267 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
4268
4269 // Remove all HTML tags and decode HTML entities
4270 $content = wp_strip_all_tags($content);
4271 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
4272
4273 // Trim and normalize whitespace
4274 $content = trim(preg_replace('/\s+/', ' ', $content));
4275
4276 return $content;
4277 }
4278
4279
4280
4281 private function mxchat_fetch_loops_mailing_lists($api_key) {
4282 $url = 'https://app.loops.so/api/v1/lists';
4283 $response = wp_remote_get($url, array(
4284 'headers' => array(
4285 'Authorization' => 'Bearer ' . $api_key,
4286 'Content-Type' => 'application/json'
4287 )
4288 ));
4289
4290 if (is_wp_error($response)) {
4291 return array();
4292 }
4293
4294 $body = wp_remote_retrieve_body($response);
4295 $lists = json_decode($body, true);
4296
4297 return isset($lists) && is_array($lists) ? $lists : array();
4298 }
4299
4300
4301
4302
4303
4304 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
4305 if (empty($vec1) || empty($vec2)) {
4306 return 0.0;
4307 }
4308
4309 $dot_product = 0.0;
4310 $norm_a = 0.0;
4311 $norm_b = 0.0;
4312
4313 for ($i = 0; $i < count($vec1); $i++) {
4314 $dot_product += $vec1[$i] * $vec2[$i];
4315 $norm_a += pow($vec1[$i], 2);
4316 $norm_b += pow($vec2[$i], 2);
4317 }
4318
4319 if ($norm_a == 0.0 || $norm_b == 0.0) {
4320 return 0.0;
4321 } else {
4322 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
4323 }
4324 }
4325
4326
4327
4328
4329 }
4330 ?>
4331