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

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