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

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

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