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

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