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

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

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