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

4,412 lines 177.3 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 // Restrict access to admins
976 if (!current_user_can('manage_options')) {
977 wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
978 }
979
980 // Fetch all transcripts ordered by session ID and timestamp
981 $chat_transcripts = $wpdb->get_results(
982 $wpdb->prepare("SELECT * FROM {$table_name} ORDER BY session_id, timestamp ASC")
983 );
984
985 if (empty($chat_transcripts)) {
986 wp_die(esc_html__('No chat history available.', 'mxchat'));
987 }
988
989 ob_start();
990
991 $current_session_id = '';
992 echo '<div class="mxchat-transcript">';
993
994 foreach ($chat_transcripts as $transcript) {
995 // Start a new session block if session_id changes
996 if ($current_session_id !== $transcript->session_id) {
997 if ($current_session_id !== '') {
998 // Close the previous session's messages
999 echo '</div></div>';
1000 }
1001
1002 // Start a new session block
1003 $current_session_id = sanitize_text_field($transcript->session_id);
1004 echo '<div class="mxchat-session">';
1005 echo '<div class="mxchat-session-header">';
1006 echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($transcript->session_id) . '"> ';
1007 echo esc_html($current_session_id);
1008 echo '</div>';
1009 echo '<div class="mxchat-messages">';
1010 }
1011
1012 // Format message timestamp with date and time
1013 $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
1014 $message_class = '';
1015 $display_role = '';
1016
1017 // Determine role and message styling
1018 switch ($transcript->role) {
1019 case 'assistant':
1020 case 'bot':
1021 $message_class = 'bot-message';
1022 $display_role = esc_html__('Chatbot', 'mxchat');
1023 break;
1024 case 'user':
1025 $message_class = 'user-message';
1026 $display_role = !empty($transcript->user_identifier)
1027 ? sanitize_text_field($transcript->user_identifier)
1028 : esc_html__('User', 'mxchat');
1029 break;
1030 case 'agent':
1031 $message_class = 'agent-message';
1032 $display_role = esc_html__('Agent', 'mxchat');
1033 break;
1034 default:
1035 $message_class = 'unknown-message';
1036 $display_role = esc_html__('Unknown', 'mxchat');
1037 }
1038
1039 // Unescape and sanitize the message content while preserving formatting
1040 $message_content = wp_kses(
1041 stripslashes($transcript->message), // Unescape content
1042 [
1043 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1044 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1045 'a' => ['href' => [], 'title' => []], // Allow links
1046 ]
1047 );
1048
1049 // Replace newlines with <br> tags to preserve spacing
1050 $message_content = nl2br($message_content);
1051
1052 // Render the message
1053 echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1054 echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1055 echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1056 echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1057 echo '</div>';
1058 }
1059
1060 // Close any remaining open tags
1061 echo '</div></div></div>';
1062
1063 $output = ob_get_clean();
1064 echo $output;
1065 wp_die();
1066 }
1067
1068
1069 public function mxchat_create_activation_page() {
1070 $license_status = get_option('mxchat_license_status', 'inactive');
1071 $license_error = get_option('mxchat_license_error', '');
1072 ?>
1073 <div class="wrap mxchat-admin">
1074 <h2>MxChat Pro: Activation</h2>
1075 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
1076 <div class="error notice">
1077 <p><?php echo esc_html($license_error); ?></p>
1078 </div>
1079 <?php endif; ?>
1080
1081 <form id="mxchat-activation-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
1082 <table class="form-table">
1083 <tr valign="top">
1084 <th scope="row">Email Address</th>
1085 <td>
1086 <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 />
1087 </td>
1088 </tr>
1089 <tr valign="top">
1090 <th scope="row">Activation Key</th>
1091 <td>
1092 <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 />
1093 </td>
1094 </tr>
1095 </table>
1096 <?php if ($license_status !== 'active'): ?>
1097 <button type="submit" id="activate_license_button" class="button button-primary"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
1098 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
1099 <?php endif; ?>
1100 </form>
1101
1102 <!-- License Status Display -->
1103 <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
1104 </div>
1105 <?php
1106 }
1107
1108 public function mxchat_intents_page_html() {
1109 if ( ! current_user_can( 'manage_options' ) ) {
1110 return;
1111 }
1112
1113 // Fetch existing intents with pagination and filtering
1114 global $wpdb;
1115 $table_name = $wpdb->prefix . 'mxchat_intents';
1116 $page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
1117 $per_page = 20;
1118 $offset = ( $page - 1 ) * $per_page;
1119
1120 // Build the WHERE clause based on filters
1121 $where = '1=1';
1122 $search_term = isset( $_GET['s'] ) ? trim( $_GET['s'] ) : '';
1123 $callback_filter = isset( $_GET['callback_filter'] ) ? sanitize_text_field( $_GET['callback_filter'] ) : '';
1124
1125 if ( $search_term ) {
1126 $search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
1127 $where .= $wpdb->prepare( ' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like );
1128 }
1129
1130 if ( $callback_filter ) {
1131 $where .= $wpdb->prepare( ' AND callback_function = %s', $callback_filter );
1132 }
1133
1134 // Get total count for pagination
1135 $total_intents = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE $where" );
1136 $total_pages = ceil( $total_intents / $per_page );
1137
1138 // Fetch intents with pagination and filters
1139 $intents = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset ) );
1140
1141 // Get available callback functions with 'pro_only' flag
1142 $available_callbacks = $this->mxchat_get_available_callbacks();
1143 ?>
1144
1145 <div class="wrap mxchat-admin">
1146 <!-- Add Intent Form -->
1147 <form id="mxchat-add-intent-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
1148 <input type="hidden" name="action" value="mxchat_add_intent">
1149 <?php wp_nonce_field( 'mxchat_add_intent_nonce' ); ?>
1150 <h2><?php esc_html_e( 'Add New Intent', 'mxchat' ); ?></h2>
1151 <p class="description">
1152 <?php esc_html_e( 'We highly encourage users to quickly read our ', 'mxchat' ); ?>
1153 <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
1154 <?php esc_html_e( 'documentation', 'mxchat' ); ?>
1155 </a>
1156 <?php esc_html_e( ' to better understand intents. Some intents require setup in the Integration tab.', 'mxchat' ); ?>
1157 </p>
1158
1159 <div class="mxchat-form-group">
1160 <label for="intent_label"><?php esc_html_e( 'Intent Label (For your reference only)', 'mxchat' ); ?></label>
1161 <input name="intent_label" type="text" id="intent_label" class="regular-text" required
1162 placeholder="Example Email Capture: Newsletter Signup">
1163 </div>
1164 <div class="mxchat-form-group">
1165 <label for="phrases"><?php esc_html_e( 'Phrases (comma-separated)', 'mxchat' ); ?></label>
1166 <textarea name="phrases" id="phrases" rows="5" class="large-text" required
1167 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>
1168 </div>
1169 <div class="mxchat-form-group">
1170 <label for="callback_function"><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></label>
1171 <select name="callback_function" id="callback_function" required>
1172 <option value=""><?php esc_html_e( 'Select a Callback', 'mxchat' ); ?></option>
1173 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1174 <?php
1175 $label = $callback_data['label'];
1176 $pro_only = $callback_data['pro_only'];
1177 $disabled = ( ! $this->is_activated && $pro_only ) ? 'disabled' : '';
1178 $label_suffix = ( ! $this->is_activated && $pro_only ) ? ' (Pro Only)' : '';
1179 ?>
1180 <option value="<?php echo esc_attr( $function ); ?>" <?php echo $disabled; ?>>
1181 <?php echo esc_html( $label . $label_suffix ); ?>
1182 </option>
1183 <?php endforeach; ?>
1184 </select>
1185 </div>
1186 <button type="submit" class="button button-primary submit-content-button">
1187 <?php esc_html_e( 'Add Intent', 'mxchat' ); ?>
1188 </button>
1189 <div id="mxchat-intent-loading" style="display: none;"></div>
1190 <div id="mxchat-intent-loading-text" style="display: none;">
1191 <?php esc_html_e( 'Saving intent, please wait...', 'mxchat' ); ?>
1192 </div>
1193
1194 </form>
1195
1196 <!-- Filter Form -->
1197 <form method="get" action="">
1198 <input type="hidden" name="page" value="mxchat-intents">
1199 <div class="mxchat-search-group">
1200 <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e( 'Search Intents', 'mxchat' ); ?>" value="<?php echo esc_attr( $search_term ); ?>">
1201 <select name="callback_filter" id="mxchat-callback-filter">
1202 <option value=""><?php esc_html_e( 'All Callbacks', 'mxchat' ); ?></option>
1203 <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
1204 <?php $label = $callback_data['label']; ?>
1205 <option value="<?php echo esc_attr( $function ); ?>" <?php selected( $callback_filter, $function ); ?>><?php echo esc_html( $label ); ?></option>
1206 <?php endforeach; ?>
1207 </select>
1208 <button type="submit" class="button"><?php esc_html_e( 'Filter', 'mxchat' ); ?></button>
1209 </div>
1210 </form>
1211
1212 <!-- Intents Table -->
1213 <table class="wp-list-table mxchat-intents-table widefat fixed striped">
1214 <thead>
1215 <tr>
1216 <th><?php esc_html_e( 'Intent Label', 'mxchat' ); ?></th>
1217 <th><?php esc_html_e( 'Phrases', 'mxchat' ); ?></th>
1218 <th><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></th>
1219 <th><?php esc_html_e( 'Similarity Threshold', 'mxchat' ); ?></th>
1220 <th><?php esc_html_e( 'Actions', 'mxchat' ); ?></th>
1221 </tr>
1222 </thead>
1223 <tbody>
1224 <?php if ( $intents ) : ?>
1225 <?php foreach ( $intents as $intent ) : ?>
1226 <?php
1227 $callback_function = $intent->callback_function;
1228 $callback_label = isset( $available_callbacks[ $callback_function ]['label'] ) ? $available_callbacks[ $callback_function ]['label'] : $callback_function;
1229 $threshold_value = isset( $intent->similarity_threshold ) ? round( $intent->similarity_threshold * 100 ) : 85;
1230 ?>
1231 <tr>
1232 <td><?php echo esc_html( $intent->intent_label ); ?></td>
1233 <td><?php echo esc_html( $intent->phrases ); ?></td>
1234 <td><?php echo esc_html( $callback_label ); ?></td>
1235 <!-- Similarity Threshold Column -->
1236 <td>
1237 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
1238 <?php wp_nonce_field( 'mxchat_update_intent_threshold_nonce' ); ?>
1239 <input type="hidden" name="action" value="mxchat_update_intent_threshold">
1240 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1241 <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 + '%'">
1242 <output id="threshold_output_<?php echo esc_attr( $intent->id ); ?>"><?php echo esc_html( $threshold_value ); ?>%</output>
1243 </td>
1244 <!-- Actions Column -->
1245 <td>
1246 <button type="submit" class="button button-primary mxchat-save-button">
1247 <?php esc_html_e( 'Save', 'mxchat' ); ?>
1248 </button>
1249 </form>
1250 <!-- Delete Form -->
1251 <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' ); ?>');">
1252 <?php wp_nonce_field( 'mxchat_delete_intent_nonce' ); ?>
1253 <input type="hidden" name="action" value="mxchat_delete_intent">
1254 <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
1255 <button type="submit" class="button mxchat-delete-all">
1256 <?php esc_html_e( 'Delete', 'mxchat' ); ?>
1257 </button>
1258 </form>
1259 </td>
1260 </tr>
1261 <?php endforeach; ?>
1262 <?php else : ?>
1263 <tr>
1264 <td colspan="5"><?php esc_html_e( 'No intents found.', 'mxchat' ); ?></td>
1265 </tr>
1266 <?php endif; ?>
1267 </tbody>
1268 </table>
1269 </div>
1270 <?php
1271 }
1272
1273
1274 public function mxchat_handle_update_intent_threshold() {
1275 if ( ! current_user_can( 'manage_options' ) ) {
1276 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1277 }
1278
1279 check_admin_referer('mxchat_update_intent_threshold_nonce');
1280
1281 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
1282 global $wpdb;
1283 $table_name = $wpdb->prefix . 'mxchat_intents';
1284 $intent_id = intval($_POST['intent_id']);
1285 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
1286 $similarity_threshold = $threshold_percentage / 100;
1287
1288 $wpdb->update(
1289 $table_name,
1290 ['similarity_threshold' => $similarity_threshold],
1291 ['id' => $intent_id],
1292 ['%f'],
1293 ['%d']
1294 );
1295 }
1296
1297 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1298 exit;
1299 }
1300
1301
1302 public function mxchat_handle_add_intent() {
1303 if ( ! current_user_can( 'manage_options' ) ) {
1304 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1305 }
1306
1307 check_admin_referer('mxchat_add_intent_nonce');
1308
1309 global $wpdb;
1310 $table_name = $wpdb->prefix . 'mxchat_intents';
1311
1312 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
1313 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
1314 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
1315 $default_threshold = 0.85;
1316
1317 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
1318 wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
1319 }
1320
1321 $available_callbacks = $this->mxchat_get_available_callbacks();
1322
1323 if (!array_key_exists($callback_function, $available_callbacks)) {
1324 wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
1325 }
1326
1327 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
1328 if ($is_pro_only && !$this->is_activated) {
1329 wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
1330 }
1331
1332 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
1333
1334 if (empty($phrases_array)) {
1335 wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
1336 }
1337
1338 $vectors = [];
1339 foreach ($phrases_array as $phrase) {
1340 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
1341 if (is_array($embedding_vector)) {
1342 $vectors[] = $embedding_vector;
1343 } else {
1344 wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
1345 }
1346 }
1347
1348 if (!empty($vectors)) {
1349 $combined_vector = $this->mxchat_average_vectors($vectors);
1350 $serialized_vector = maybe_serialize($combined_vector);
1351
1352 $result = $wpdb->insert($table_name, [
1353 'intent_label' => $intent_label,
1354 'phrases' => implode(', ', $phrases_array),
1355 'embedding_vector' => $serialized_vector,
1356 'callback_function' => $callback_function,
1357 'similarity_threshold' => $default_threshold,
1358 ]);
1359
1360 if ($result === false) {
1361 wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
1362 }
1363 } else {
1364 wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
1365 }
1366
1367 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1368 exit;
1369 }
1370
1371
1372
1373
1374
1375
1376 private function mxchat_get_available_callbacks() {
1377 return [
1378 'mxchat_handle_email_capture' => [
1379 'label' => 'Email Capture',
1380 'pro_only' => false,
1381 ],
1382 'mxchat_handle_product_inquiry' => [
1383 'label' => 'Show Product Card',
1384 'pro_only' => true,
1385 ],
1386 'mxchat_handle_order_history' => [
1387 'label' => 'Order History',
1388 'pro_only' => true,
1389 ],
1390 'mxchat_generate_image' => [
1391 'label' => 'Generate Image',
1392 'pro_only' => true,
1393 ],
1394 'mxchat_handle_search_request' => [
1395 'label' => 'Brave Web Search',
1396 'pro_only' => false,
1397 ],
1398 'mxchat_handle_image_search_request' => [
1399 'label' => 'Brave Image Search',
1400 'pro_only' => false,
1401 ],
1402 'mxchat_handle_add_to_cart_intent' => [
1403 'label' => 'Add to Cart',
1404 'pro_only' => true,
1405 ],
1406 'mxchat_handle_checkout_intent' => [
1407 'label' => 'Proceed to Checkout',
1408 'pro_only' => true,
1409 ],
1410 'mxchat_handle_pdf_discussion' => [
1411 'label' => 'Chat with PDF',
1412 'pro_only' => true,
1413 ],
1414 'mxchat_handle_product_recommendations' => [
1415 'label' => 'Product Recommendations',
1416 'pro_only' => true,
1417 ],
1418 'mxchat_live_agent_handover' => [
1419 'label' => 'Live Agent',
1420 'pro_only' => true,
1421 ],
1422 'mxchat_handle_switch_to_chatbot_intent' => [
1423 'label' => 'Back to Chatbot',
1424 'pro_only' => true,
1425 ],
1426 ];
1427 }
1428
1429
1430 private function mxchat_average_vectors($vectors) {
1431 $vector_length = count($vectors[0]);
1432 $sum_vector = array_fill(0, $vector_length, 0);
1433
1434 foreach ($vectors as $vector) {
1435 for ($i = 0; $i < $vector_length; $i++) {
1436 $sum_vector[$i] += $vector[$i];
1437 }
1438 }
1439
1440 // Divide each component by the number of vectors to get the average
1441 $num_vectors = count($vectors);
1442 for ($i = 0; $i < $vector_length; $i++) {
1443 $sum_vector[$i] /= $num_vectors;
1444 }
1445
1446 return $sum_vector;
1447 }
1448
1449 public function mxchat_handle_delete_intent() {
1450 if ( ! current_user_can( 'manage_options' ) ) {
1451 wp_die( esc_html__('Unauthorized user', 'mxchat') );
1452 }
1453
1454 check_admin_referer('mxchat_delete_intent_nonce');
1455
1456 if (isset($_POST['intent_id'])) {
1457 global $wpdb;
1458 $table_name = $wpdb->prefix . 'mxchat_intents';
1459 $intent_id = intval($_POST['intent_id']);
1460
1461 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
1462 }
1463
1464 wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
1465 exit;
1466 }
1467
1468
1469 public function mxchat_page_init() {
1470 // Register settings
1471 register_setting(
1472 'mxchat_option_group',
1473 'mxchat_options',
1474 array($this, 'mxchat_sanitize')
1475 );
1476
1477 register_setting(
1478 'mxchat_option_group',
1479 'mxchat_similarity_threshold',
1480 array(
1481 'type' => 'number',
1482 'sanitize_callback' => function($value) {
1483 $value = absint($value); // Ensure it's an integer
1484 return min(max($value, 70), 85); // Enforce range
1485 },
1486 'default' => 80,
1487 )
1488 );
1489
1490
1491 // Chatbot Settings Section
1492 add_settings_section(
1493 'mxchat_chatbot_section',
1494 'Chatbot Settings',
1495 null,
1496 'mxchat-chatbot'
1497 );
1498
1499 // Similarity Threshold Slider
1500 add_settings_field(
1501 'similarity_threshold', // Field ID
1502 'Similarity Threshold', // Field title
1503 array($this, 'mxchat_similarity_threshold_callback'), // Callback function
1504 'mxchat-chatbot', // Page
1505 'mxchat_chatbot_section' // Section
1506 );
1507
1508 // Existing fields...
1509 add_settings_field(
1510 'api_key',
1511 'OpenAI API Key',
1512 array($this, 'api_key_callback'),
1513 'mxchat-chatbot',
1514 'mxchat_chatbot_section'
1515 );
1516
1517 add_settings_field(
1518 'xai_api_key',
1519 'X.AI API Key',
1520 array($this, 'xai_api_key_callback'),
1521 'mxchat-chatbot',
1522 'mxchat_chatbot_section'
1523 );
1524
1525 add_settings_field(
1526 'claude_api_key',
1527 'Claude API Key',
1528 array($this, 'claude_api_key_callback'),
1529 'mxchat-chatbot',
1530 'mxchat_chatbot_section'
1531 );
1532
1533
1534 add_settings_field(
1535 'system_prompt_instructions',
1536 'AI Instructions',
1537 array($this, 'system_prompt_instructions_callback'),
1538 'mxchat-chatbot',
1539 'mxchat_chatbot_section'
1540 );
1541
1542 add_settings_field(
1543 'model',
1544 'Model',
1545 array($this, 'mxchat_model_callback'),
1546 'mxchat-chatbot',
1547 'mxchat_chatbot_section'
1548 );
1549
1550 add_settings_field(
1551 'top_bar_title',
1552 'Top Bar Title',
1553 array($this, 'mxchat_top_bar_title_callback'),
1554 'mxchat-chatbot',
1555 'mxchat_chatbot_section'
1556 );
1557
1558 add_settings_field(
1559 'intro_message',
1560 'Introductory Message',
1561 array($this, 'mxchat_intro_message_callback'),
1562 'mxchat-chatbot',
1563 'mxchat_chatbot_section'
1564 );
1565
1566 add_settings_field(
1567 'input_copy',
1568 'Input Copy',
1569 array($this, 'mxchat_input_copy_callback'),
1570 'mxchat-chatbot',
1571 'mxchat_chatbot_section'
1572 );
1573
1574 add_settings_field(
1575 'rate_limit_logged_in',
1576 __('Rate Limit for Logged-in Users', 'mxchat'),
1577 array($this, 'mxchat_rate_limit_logged_in_callback'),
1578 'mxchat-chatbot',
1579 'mxchat_chatbot_section'
1580 );
1581
1582 add_settings_field(
1583 'rate_limit_logged_out',
1584 __('Rate Limit for Logged-out Users', 'mxchat'),
1585 array($this, 'mxchat_rate_limit_logged_out_callback'),
1586 'mxchat-chatbot',
1587 'mxchat_chatbot_section'
1588 );
1589
1590 add_settings_field(
1591 'rate_limit_message',
1592 'Rate Limit Message',
1593 array($this, 'mxchat_rate_limit_message_callback'),
1594 'mxchat-chatbot',
1595 'mxchat_chatbot_section'
1596 );
1597
1598 add_settings_field(
1599 'pre_chat_message',
1600 'Pre-Chat Message',
1601 array($this, 'mxchat_pre_chat_message_callback'),
1602 'mxchat-chatbot',
1603 'mxchat_chatbot_section'
1604 );
1605
1606 add_settings_field(
1607 'append_to_body',
1608 'Append Chat Widget to Body',
1609 array($this, 'mxchat_append_to_body_callback'),
1610 'mxchat-chatbot',
1611 'mxchat_chatbot_section'
1612 );
1613
1614 add_settings_field(
1615 'privacy_toggle',
1616 'Toggle Privacy Notice',
1617 array($this, 'mxchat_privacy_toggle_callback'),
1618 'mxchat-chatbot',
1619 'mxchat_chatbot_section'
1620 );
1621
1622 add_settings_field(
1623 'complianz_toggle',
1624 'Enable Complianz',
1625 array($this, 'mxchat_complianz_toggle_callback'),
1626 'mxchat-chatbot',
1627 'mxchat_chatbot_section'
1628 );
1629
1630 add_settings_field(
1631 'link_target_toggle',
1632 'Open Links in a New Tab',
1633 array($this, 'mxchat_link_target_toggle_callback'),
1634 'mxchat-chatbot',
1635 'mxchat_chatbot_section'
1636 );
1637
1638 add_settings_field(
1639 'chat_persistence_toggle',
1640 'Enable Chat Persistence',
1641 array($this, 'mxchat_chat_persistence_toggle_callback'),
1642 'mxchat-chatbot',
1643 'mxchat_chatbot_section'
1644 );
1645
1646 add_settings_field(
1647 'popular_question_1',
1648 'Popular Question 1',
1649 array($this, 'mxchat_popular_question_1_callback'),
1650 'mxchat-chatbot',
1651 'mxchat_chatbot_section'
1652 );
1653
1654 add_settings_field(
1655 'popular_question_2',
1656 'Popular Question 2',
1657 array($this, 'mxchat_popular_question_2_callback'),
1658 'mxchat-chatbot',
1659 'mxchat_chatbot_section'
1660 );
1661
1662 add_settings_field(
1663 'popular_question_3',
1664 'Popular Question 3',
1665 array($this, 'mxchat_popular_question_3_callback'),
1666 'mxchat-chatbot',
1667 'mxchat_chatbot_section'
1668 );
1669
1670 add_settings_field(
1671 'additional_popular_questions',
1672 'Additional Popular Questions',
1673 array($this, 'mxchat_additional_popular_questions_callback'),
1674 'mxchat-chatbot',
1675 'mxchat_chatbot_section'
1676 );
1677
1678
1679 // WooCommerce Settings Section
1680 add_settings_section(
1681 'mxchat_woocommerce_section',
1682 'WooCommerce Settings',
1683 null,
1684 'mxchat-embed'
1685 );
1686
1687 // Loops Settings Section
1688 add_settings_section(
1689 'mxchat_loops_section',
1690 'Loops Settings',
1691 null,
1692 'mxchat-embed'
1693 );
1694
1695 // WooCommerce Settings Fields
1696 add_settings_field(
1697 'enable_woocommerce_integration',
1698 'Automatically Embed Products',
1699 array($this, 'mxchat_enable_woocommerce_integration_callback'),
1700 'mxchat-embed',
1701 'mxchat_woocommerce_section'
1702 );
1703
1704
1705 add_settings_field(
1706 'woocommerce_consumer_key',
1707 'WooCommerce Consumer Key',
1708 array($this, 'mxchat_woocommerce_consumer_key_callback'),
1709 'mxchat-embed',
1710 'mxchat_woocommerce_section'
1711 );
1712
1713 add_settings_field(
1714 'woocommerce_consumer_secret',
1715 'WooCommerce Consumer Secret',
1716 array($this, 'mxchat_woocommerce_consumer_secret_callback'),
1717 'mxchat-embed',
1718 'mxchat_woocommerce_section'
1719 );
1720
1721 // Loops Settings Fields
1722 add_settings_field(
1723 'loops_api_key',
1724 'Loops API Key',
1725 array($this, 'mxchat_loops_api_key_callback'),
1726 'mxchat-embed',
1727 'mxchat_loops_section'
1728 );
1729
1730 add_settings_field(
1731 'loops_mailing_list',
1732 'Loops Mailing List',
1733 array($this, 'mxchat_loops_mailing_list_callback'),
1734 'mxchat-embed',
1735 'mxchat_loops_section'
1736 );
1737
1738 add_settings_field(
1739 'triggered_phrase_response',
1740 'Triggered Phrase Response',
1741 array($this, 'mxchat_triggered_phrase_response_callback'),
1742 'mxchat-embed',
1743 'mxchat_loops_section'
1744 );
1745
1746 add_settings_field(
1747 'email_capture_response',
1748 'Email Capture Response',
1749 array($this, 'mxchat_email_capture_response_callback'),
1750 'mxchat-embed',
1751 'mxchat_loops_section'
1752 );
1753
1754
1755 // Brave Search Settings Fields
1756 add_settings_section(
1757 'mxchat_brave_section',
1758 __('Brave Search Settings', 'mxchat'),
1759 array($this, 'mxchat_brave_section_callback'),
1760 'mxchat-embed'
1761 );
1762
1763 add_settings_field(
1764 'brave_api_key',
1765 __('Brave API Key', 'mxchat'),
1766 array($this, 'mxchat_brave_api_key_callback'),
1767 'mxchat-embed',
1768 'mxchat_brave_section'
1769 );
1770
1771 add_settings_field(
1772 'brave_image_count',
1773 __('Number of Images to Return', 'mxchat'),
1774 array($this, 'mxchat_brave_image_count_callback'),
1775 'mxchat-embed',
1776 'mxchat_brave_section'
1777 );
1778
1779 add_settings_field(
1780 'brave_safe_search',
1781 __('Safe Search', 'mxchat'),
1782 array($this, 'mxchat_brave_safe_search_callback'),
1783 'mxchat-embed',
1784 'mxchat_brave_section'
1785 );
1786
1787 add_settings_field(
1788 'brave_news_count',
1789 __('Number of News Articles', 'mxchat'),
1790 array($this, 'mxchat_brave_news_count_callback'),
1791 'mxchat-embed',
1792 'mxchat_brave_section'
1793 );
1794
1795 add_settings_field(
1796 'brave_country',
1797 __('Country', 'mxchat'),
1798 array($this, 'mxchat_brave_country_callback'),
1799 'mxchat-embed',
1800 'mxchat_brave_section'
1801 );
1802
1803 add_settings_field(
1804 'brave_language',
1805 __('Language', 'mxchat'),
1806 array($this, 'mxchat_brave_language_callback'),
1807 'mxchat-embed',
1808 'mxchat_brave_section'
1809 );
1810
1811
1812 // Chat with PDF Intent Settings Fields
1813 add_settings_section(
1814 'mxchat_pdf_intent_section',
1815 __('Toolbar Settings & Intents', 'mxchat'),
1816 array($this, 'mxchat_pdf_intent_section_callback'),
1817 'mxchat-embed'
1818 );
1819
1820 add_settings_field(
1821 'chat_toolbar_toggle',
1822 __('Show Chat Toolbar', 'mxchat'),
1823 array($this, 'mxchat_chat_toolbar_toggle_callback'),
1824 'mxchat-embed',
1825 'mxchat_pdf_intent_section'
1826 );
1827
1828
1829 add_settings_field(
1830 'pdf_intent_trigger_text',
1831 __('Intent Trigger Text', 'mxchat'),
1832 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
1833 'mxchat-embed',
1834 'mxchat_pdf_intent_section'
1835 );
1836
1837 add_settings_field(
1838 'pdf_intent_success_text',
1839 __('Success Text', 'mxchat'),
1840 array($this, 'mxchat_pdf_intent_success_text_callback'),
1841 'mxchat-embed',
1842 'mxchat_pdf_intent_section'
1843 );
1844
1845 add_settings_field(
1846 'pdf_intent_error_text',
1847 __('Error Text', 'mxchat'),
1848 array($this, 'mxchat_pdf_intent_error_text_callback'),
1849 'mxchat-embed',
1850 'mxchat_pdf_intent_section'
1851 );
1852
1853 // Add PDF Maximum Pages Field
1854 add_settings_field(
1855 'pdf_max_pages',
1856 __('Maximum PDF Pages', 'mxchat'),
1857 array($this, 'mxchat_pdf_max_pages_callback'),
1858 'mxchat-embed',
1859 'mxchat_pdf_intent_section'
1860 );
1861
1862
1863
1864
1865 // Live Agent Settings Fields
1866 add_settings_section(
1867 'mxchat_live_agent_section',
1868 __('Live Agent Settings', 'mxchat'),
1869 array($this, 'mxchat_live_agent_section_callback'),
1870 'mxchat-embed'
1871 );
1872
1873 // Live Agent Status Fields (add at top of live agent settings)
1874 add_settings_field(
1875 'live_agent_status',
1876 __('Live Agent Status', 'mxchat'),
1877 array($this, 'mxchat_live_agent_status_callback'),
1878 'mxchat-embed',
1879 'mxchat_live_agent_section'
1880 );
1881
1882 add_settings_field(
1883 'live_agent_notification_message',
1884 __('Notification Message', 'mxchat'),
1885 array($this, 'mxchat_live_agent_notification_message_callback'),
1886 'mxchat-embed',
1887 'mxchat_live_agent_section'
1888 );
1889
1890 add_settings_field(
1891 'live_agent_away_message',
1892 __('Away Message', 'mxchat'),
1893 array($this, 'mxchat_live_agent_away_message_callback'),
1894 'mxchat-embed',
1895 'mxchat_live_agent_section'
1896 );
1897
1898 add_settings_field(
1899 'live_agent_webhook_url',
1900 __('Slack Webhook URL', 'mxchat'),
1901 array($this, 'mxchat_live_agent_webhook_url_callback'),
1902 'mxchat-embed',
1903 'mxchat_live_agent_section'
1904 );
1905
1906 add_settings_field(
1907 'live_agent_secret_key',
1908 __('Slack Secret Key', 'mxchat'),
1909 array($this, 'mxchat_live_agent_secret_key_callback'),
1910 'mxchat-embed',
1911 'mxchat_live_agent_section'
1912 );
1913
1914 // Live Agent Integration Fields
1915 add_settings_field(
1916 'live_agent_bot_token',
1917 __('Slack Bot OAuth Token', 'mxchat'),
1918 array($this, 'mxchat_live_agent_bot_token_callback'),
1919 'mxchat-embed',
1920 'mxchat_live_agent_section'
1921 );
1922
1923
1924 // Theme Settings Section
1925 add_settings_section(
1926 'mxchat_theme_section',
1927 'Theme Settings',
1928 null,
1929 'mxchat-theme'
1930 );
1931
1932 add_settings_field(
1933 'close_button_color',
1934 'Close Button & Title Color',
1935 array($this, 'mxchat_close_button_color_callback'),
1936 'mxchat-theme',
1937 'mxchat_theme_section'
1938 );
1939
1940 add_settings_field(
1941 'chatbot_bg_color',
1942 'Chatbot Background Color',
1943 array($this, 'mxchat_chatbot_bg_color_callback'),
1944 'mxchat-theme',
1945 'mxchat_theme_section'
1946 );
1947
1948 add_settings_field(
1949 'user_message_bg_color',
1950 'User Message Background Color',
1951 array($this, 'mxchat_user_message_bg_color_callback'),
1952 'mxchat-theme',
1953 'mxchat_theme_section'
1954 );
1955
1956 add_settings_field(
1957 'user_message_font_color',
1958 'User Message Font Color',
1959 array($this, 'mxchat_user_message_font_color_callback'),
1960 'mxchat-theme',
1961 'mxchat_theme_section'
1962 );
1963
1964 add_settings_field(
1965 'bot_message_bg_color',
1966 'Bot Message Background Color',
1967 array($this, 'mxchat_bot_message_bg_color_callback'),
1968 'mxchat-theme',
1969 'mxchat_theme_section'
1970 );
1971
1972 add_settings_field(
1973 'bot_message_font_color',
1974 'Bot Message Font Color',
1975 array($this, 'mxchat_bot_message_font_color_callback'),
1976 'mxchat-theme',
1977 'mxchat_theme_section'
1978 );
1979
1980 add_settings_field(
1981 'top_bar_bg_color',
1982 'Top Bar Background Color',
1983 array($this, 'mxchat_top_bar_bg_color_callback'),
1984 'mxchat-theme',
1985 'mxchat_theme_section'
1986 );
1987
1988 add_settings_field(
1989 'send_button_font_color',
1990 'Send Button Color',
1991 array($this, 'mxchat_send_button_font_color_callback'),
1992 'mxchat-theme',
1993 'mxchat_theme_section'
1994 );
1995
1996 add_settings_field(
1997 'chat_input_font_color',
1998 'Chat Input Font Color',
1999 array($this, 'mxchat_chat_input_font_color_callback'),
2000 'mxchat-theme',
2001 'mxchat_theme_section'
2002 );
2003
2004 add_settings_field(
2005 'chatbot_background_color',
2006 'Floating Widget Background Color',
2007 array($this, 'mxchat_chatbot_background_color_callback'),
2008 'mxchat-theme',
2009 'mxchat_theme_section'
2010 );
2011
2012 add_settings_field(
2013 'icon_color',
2014 'Chatbot Icon Color',
2015 array($this, 'mxchat_icon_color_callback'),
2016 'mxchat-theme',
2017 'mxchat_theme_section'
2018 );
2019
2020 add_settings_field(
2021 'custom_icon',
2022 'Custom Chatbot Icon (PNG)',
2023 array($this, 'mxchat_custom_icon_callback'),
2024 'mxchat-theme',
2025 'mxchat_theme_section'
2026 );
2027
2028
2029 add_settings_field(
2030 'live_agent_message_bg_color',
2031 'Live Agent Background Color',
2032 array($this, 'mxchat_live_agent_message_bg_color_callback'),
2033 'mxchat-theme',
2034 'mxchat_theme_section'
2035 );
2036
2037 add_settings_field(
2038 'live_agent_message_font_color',
2039 'Live Agent Font Color',
2040 array($this, 'mxchat_live_agent_message_font_color_callback'),
2041 'mxchat-theme',
2042 'mxchat_theme_section'
2043 );
2044
2045 // Mode Indicator Background Color
2046 add_settings_field(
2047 'mode_indicator_bg_color',
2048 'Mode Indicator Background Color',
2049 array($this, 'mxchat_mode_indicator_bg_color_callback'),
2050 'mxchat-theme',
2051 'mxchat_theme_section'
2052 );
2053
2054 // Mode Indicator Font Color
2055 add_settings_field(
2056 'mode_indicator_font_color',
2057 'Mode Indicator Font Color',
2058 array($this, 'mxchat_mode_indicator_font_color_callback'),
2059 'mxchat-theme',
2060 'mxchat_theme_section'
2061 );
2062
2063 add_settings_field(
2064 'toolbar_icon_color',
2065 'Toolbar Icon Color',
2066 array($this, 'mxchat_toolbar_icon_color_callback'),
2067 'mxchat-theme',
2068 'mxchat_theme_section'
2069 );
2070
2071 // General Settings Section
2072 add_settings_section(
2073 'mxchat_general_section',
2074 'Frequently Asked Questions (FAQ)',
2075 null,
2076 'mxchat-general'
2077 );
2078
2079
2080
2081
2082
2083
2084 }
2085
2086 public function mxchat_handle_activate_license() {
2087 check_ajax_referer('mxchat_activate_license_nonce', 'security');
2088
2089 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
2090 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
2091
2092 if (empty($license_key) || empty($customer_email)) {
2093 wp_send_json_error('Email or License Key is missing');
2094 }
2095
2096 $product_id = 'MxChatPRO';
2097
2098 $response = wp_remote_get("http://mxchat.ai/?wc-api=software-api&request=activation&email={$customer_email}&license_key={$license_key}&product_id={$product_id}");
2099
2100 if (is_wp_error($response)) {
2101 wp_send_json_error('Activation failed due to a server error');
2102 }
2103
2104 $body = wp_remote_retrieve_body($response);
2105 $data = json_decode($body);
2106
2107 if ($data && isset($data->activated) && $data->activated) {
2108 update_option('mxchat_license_status', 'active');
2109 update_option('mxchat_pro_email', $customer_email);
2110 update_option('mxchat_activation_key', $license_key);
2111 wp_send_json_success();
2112 } else {
2113 $error_message = isset($data->error) ? $data->error : 'Activation failed';
2114 update_option('mxchat_license_status', 'inactive');
2115 update_option('mxchat_license_error', $error_message);
2116 wp_send_json_error($error_message);
2117 }
2118 }
2119
2120 public function mxchat_rate_limit_logged_in_callback() {
2121 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2122 $selected_rate_limit = isset($this->options['rate_limit_logged_in']) ? $this->options['rate_limit_logged_in'] : '100';
2123
2124 echo '<div class="pro-feature-wrapper active">';
2125 echo '<select id="rate_limit_logged_in" name="mxchat_options[rate_limit_logged_in]">';
2126 foreach ($rate_limits as $limit) {
2127 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2128 }
2129 echo '</select>';
2130 echo '<p class="description">Set the rate limit for logged-in users.</p>';
2131 echo '</div>';
2132 }
2133
2134 public function mxchat_rate_limit_logged_out_callback() {
2135 $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' option
2136 $selected_rate_limit = isset($this->options['rate_limit_logged_out']) ? $this->options['rate_limit_logged_out'] : '10';
2137
2138 echo '<div class="pro-feature-wrapper active">';
2139 echo '<select id="rate_limit_logged_out" name="mxchat_options[rate_limit_logged_out]">';
2140 foreach ($rate_limits as $limit) {
2141 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
2142 }
2143 echo '</select>';
2144 echo '<p class="description">Set the rate limit for logged-out users.</p>';
2145 echo '</div>';
2146 }
2147
2148
2149 public function mxchat_rate_limit_message_callback() {
2150 // Remove the is_activated check and make it always enabled
2151 echo '<div class="pro-feature-wrapper active">';
2152 printf(
2153 '<textarea id="rate_limit_message" name="mxchat_options[rate_limit_message]" rows="3" cols="50">%s</textarea>',
2154 isset($this->options['rate_limit_message']) ? esc_textarea($this->options['rate_limit_message']) : 'Rate limit exceeded. Please try again later.'
2155 );
2156 echo '</div>';
2157 }
2158
2159
2160 public function mxchat_enable_woocommerce_integration_callback() {
2161 $checked = isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1' ? 'checked' : '';
2162 $disabled = $this->is_activated ? '' : 'disabled';
2163 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2164
2165 echo '<div class="' . esc_attr($class) . '">';
2166 echo '<label class="toggle-switch">';
2167 echo '<input type="checkbox" id="enable_woocommerce_integration" name="mxchat_options[enable_woocommerce_integration]" value="1" ' . $checked . ' ' . $disabled . '>';
2168 echo '<span class="slider"></span>';
2169 echo '</label>';
2170
2171 if (!$this->is_activated) {
2172 echo '<div class="pro-feature-overlay">';
2173 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2174 echo '</div>';
2175 }
2176
2177 echo '</div>';
2178 }
2179
2180
2181
2182
2183 private function mxchat_add_option_field($id, $title, $callback = '') {
2184 add_settings_field(
2185 $id,
2186 $title,
2187 $callback ? array($this, $callback) : array($this, $id . '_callback'),
2188 'mxchat-max',
2189 'mxchat_setting_section_id',
2190 $id === 'model' ? ['label_for' => 'model'] : []
2191 );
2192 }
2193
2194
2195 public function api_key_callback() {
2196 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
2197 echo '<input type="password" id="api_key" name="mxchat_options[api_key]" value="' . $apiKey . '" class="regular-text" />';
2198 echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
2199 }
2200
2201 public function xai_api_key_callback() {
2202 // Check if the feature is activated (paid feature)
2203 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2204 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2205
2206 // Render the input field for the X.AI API key
2207 echo '<div class="' . esc_attr($class) . '">';
2208 printf(
2209 '<input type="password" id="xai_api_key" name="mxchat_options[xai_api_key]" value="%s" class="regular-text" %s />',
2210 isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '',
2211 $disabled
2212 );
2213 echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
2214
2215 // If the feature is not activated, show the overlay with a "Pro Only" message
2216 if (!$this->is_activated) {
2217 echo '<div class="pro-feature-overlay">';
2218 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2219 echo '</div>';
2220 }
2221
2222 echo '</div>';
2223 }
2224
2225 public function claude_api_key_callback() {
2226 // Check if the feature is activated (paid feature)
2227 $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
2228 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
2229
2230 // Render the input field for the Claude API key
2231 echo '<div class="' . esc_attr($class) . '">';
2232 printf(
2233 '<input type="password" id="claude_api_key" name="mxchat_options[claude_api_key]" value="%s" class="regular-text" %s />',
2234 isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '',
2235 $disabled
2236 );
2237 echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
2238
2239 // If the feature is not activated, show the overlay with a "Pro Only" message
2240 if (!$this->is_activated) {
2241 echo '<div class="pro-feature-overlay">';
2242 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2243 echo '</div>';
2244 }
2245
2246 echo '</div>';
2247 }
2248
2249 public function mxchat_woocommerce_consumer_key_callback() {
2250 $disabled = $this->is_activated ? '' : 'disabled';
2251 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2252
2253 echo '<div class="' . esc_attr($class) . '">';
2254 printf(
2255 '<input type="text" id="woocommerce_consumer_key" name="mxchat_options[woocommerce_consumer_key]" value="%s" class="regular-text" %s />',
2256 isset($this->options['woocommerce_consumer_key']) ? esc_attr($this->options['woocommerce_consumer_key']) : '',
2257 $disabled
2258 );
2259
2260 if (!$this->is_activated) {
2261 echo '<div class="pro-feature-overlay">';
2262 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2263 echo '</div>';
2264 }
2265
2266 echo '</div>';
2267 }
2268
2269 public function mxchat_woocommerce_consumer_secret_callback() {
2270 $disabled = $this->is_activated ? '' : 'disabled';
2271 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2272
2273 echo '<div class="' . esc_attr($class) . '">';
2274 printf(
2275 '<input type="password" id="woocommerce_consumer_secret" name="mxchat_options[woocommerce_consumer_secret]" value="%s" class="regular-text" %s />',
2276 isset($this->options['woocommerce_consumer_secret']) ? esc_attr($this->options['woocommerce_consumer_secret']) : '',
2277 $disabled
2278 );
2279 echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
2280
2281 if (!$this->is_activated) {
2282 echo '<div class="pro-feature-overlay">';
2283 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2284 echo '</div>';
2285 }
2286
2287 echo '</div>';
2288 }
2289
2290 public function mxchat_loops_api_key_callback() {
2291 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
2292
2293 echo '<div class="api-key-wrapper">';
2294 printf(
2295 '<input type="password" id="loops_api_key" name="mxchat_options[loops_api_key]" value="%s" class="regular-text" />',
2296 $loops_api_key
2297 );
2298 echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
2299 echo '</div>';
2300 echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
2301 }
2302
2303 public function mxchat_loops_mailing_list_callback() {
2304 // Retrieve Loops API key and lists
2305 $loops_api_key = isset($this->options['loops_api_key']) ? $this->options['loops_api_key'] : '';
2306 $selected_list = isset($this->options['loops_mailing_list']) ? $this->options['loops_mailing_list'] : '';
2307
2308 if ($loops_api_key) {
2309 // Fetch lists from Loops API
2310 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
2311
2312 if ($lists) {
2313 echo '<select id="loops_mailing_list" name="mxchat_options[loops_mailing_list]">';
2314 foreach ($lists as $list) {
2315 echo '<option value="' . esc_attr($list['id']) . '" ' . selected($selected_list, $list['id'], false) . '>' . esc_html($list['name']) . '</option>';
2316 }
2317 echo '</select>';
2318 } else {
2319 echo '<p class="description">No lists found. Please verify your API Key.</p>';
2320 }
2321 } else {
2322 echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
2323 }
2324 }
2325
2326 public function mxchat_triggered_phrase_response_callback() {
2327 $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : '';
2328 echo '<textarea id="triggered_phrase_response" name="mxchat_options[triggered_phrase_response]" rows="3" cols="50">' . esc_textarea($triggered_response) . '</textarea>';
2329 echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
2330 }
2331
2332 public function mxchat_email_capture_response_callback() {
2333 $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.';
2334 echo '<textarea id="email_capture_response" name="mxchat_options[email_capture_response]" rows="3" cols="50">' . esc_textarea($email_capture_response) . '</textarea>';
2335 echo '<p class="description">Enter the message to send when a user provides their email.</p>';
2336 }
2337
2338
2339 public function mxchat_pre_chat_message_callback() {
2340 printf(
2341 '<textarea id="pre_chat_message" name="mxchat_options[pre_chat_message]" rows="5" cols="50">%s</textarea>',
2342 isset($this->options['pre_chat_message']) ? esc_textarea($this->options['pre_chat_message']) : ''
2343 );
2344 }
2345
2346 // Callback for AI Instructions textarea
2347 public function system_prompt_instructions_callback() {
2348 printf(
2349 '<textarea id="system_prompt_instructions" name="mxchat_options[system_prompt_instructions]" rows="5" cols="50">%s</textarea>',
2350 isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''
2351 );
2352 }
2353
2354 public function mxchat_model_callback() {
2355 $models = array(
2356 'X.AI Models' => array(
2357 'grok-beta' => 'grok-beta (Early Beta)'
2358 ),
2359 'Claude Models' => array(
2360 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
2361 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
2362 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
2363 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
2364 ),
2365 'OpenAI Models' => array(
2366 'gpt-4o' => 'GPT-4o (Recommended)',
2367 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
2368 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
2369 'gpt-4' => 'GPT-4 (High Intelligence)',
2370 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
2371 )
2372 );
2373
2374 $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';
2375
2376 echo '<select id="model" name="mxchat_options[model]">';
2377
2378 foreach ($models as $group_label => $group_models) {
2379 echo '<optgroup label="' . esc_attr($group_label) . '">';
2380
2381 foreach ($group_models as $model_value => $model_label) {
2382 // Disable if not activated for paid models (Claude or X.AI)
2383 $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
2384 $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
2385
2386 // Output the <option> element
2387 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
2388 }
2389
2390 echo '</optgroup>';
2391 }
2392
2393 echo '</select>';
2394 }
2395
2396
2397
2398 public function mxchat_top_bar_title_callback() {
2399 printf(
2400 '<input type="text" id="top_bar_title" name="mxchat_options[top_bar_title]" value="%s" />',
2401 isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''
2402 );
2403 }
2404
2405 public function mxchat_intro_message_callback() {
2406 printf(
2407 '<textarea id="intro_message" name="mxchat_options[intro_message]" rows="5" cols="50">%s</textarea>',
2408 isset($this->options['intro_message']) ? esc_textarea($this->options['intro_message']) : 'Hello! How can I assist you today?'
2409 );
2410 }
2411
2412 public function mxchat_input_copy_callback() {
2413 printf(
2414 '<input type="text" id="input_copy" name="mxchat_options[input_copy]" value="%s" placeholder="How can I assist?" />',
2415 isset($this->options['input_copy']) ? esc_attr($this->options['input_copy']) : 'How can I assist?'
2416 );
2417 echo '<p class="description">This is the placeholder text for the chat input field.</p>';
2418 }
2419
2420
2421
2422
2423
2424
2425 public function mxchat_close_button_color_callback() {
2426 $disabled = $this->is_activated ? '' : 'disabled';
2427
2428 echo '<div class="pro-feature-wrapper">';
2429 printf(
2430 '<input type="text" id="close_button_color" name="mxchat_options[close_button_color]" value="%s" class="my-color-field" data-default-color="#4a4a4a" %s />',
2431 isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '',
2432 esc_attr($disabled)
2433 );
2434
2435 if (!$this->is_activated) {
2436 echo '<div class="pro-feature-overlay">';
2437 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2438 echo '</div>';
2439 }
2440
2441 echo '</div>';
2442 }
2443
2444 public function mxchat_chatbot_bg_color_callback() {
2445 $disabled = $this->is_activated ? '' : 'disabled';
2446
2447 echo '<div class="pro-feature-wrapper">';
2448 printf(
2449 '<input type="text" id="chatbot_bg_color" name="mxchat_options[chatbot_bg_color]" value="%s" class="my-color-field" data-default-color="#f9f9f9" %s />',
2450 isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '',
2451 esc_attr($disabled)
2452 );
2453
2454 if (!$this->is_activated) {
2455 echo '<div class="pro-feature-overlay">';
2456 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2457 echo '</div>';
2458 }
2459
2460 echo '</div>';
2461 }
2462
2463 public function mxchat_user_message_bg_color_callback() {
2464 $disabled = $this->is_activated ? '' : 'disabled';
2465
2466 echo '<div class="pro-feature-wrapper">';
2467 printf(
2468 '<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 />',
2469 isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '',
2470 esc_attr($disabled)
2471 );
2472
2473 if (!$this->is_activated) {
2474 echo '<div class="pro-feature-overlay">';
2475 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2476 echo '</div>';
2477 }
2478
2479 echo '</div>';
2480 }
2481
2482 public function mxchat_user_message_font_color_callback() {
2483 $disabled = $this->is_activated ? '' : 'disabled';
2484
2485 echo '<div class="pro-feature-wrapper">';
2486 printf(
2487 '<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 />',
2488 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '',
2489 esc_attr($disabled)
2490 );
2491
2492 if (!$this->is_activated) {
2493 echo '<div class="pro-feature-overlay">';
2494 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2495 echo '</div>';
2496 }
2497
2498 echo '</div>';
2499 }
2500
2501 public function mxchat_bot_message_bg_color_callback() {
2502 $disabled = $this->is_activated ? '' : 'disabled';
2503
2504 echo '<div class="pro-feature-wrapper">';
2505 printf(
2506 '<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 />',
2507 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '',
2508 esc_attr($disabled)
2509 );
2510
2511 if (!$this->is_activated) {
2512 echo '<div class="pro-feature-overlay">';
2513 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2514 echo '</div>';
2515 }
2516
2517 echo '</div>';
2518 }
2519
2520 public function mxchat_bot_message_font_color_callback() {
2521 $disabled = $this->is_activated ? '' : 'disabled';
2522
2523 echo '<div class="pro-feature-wrapper">';
2524 printf(
2525 '<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 />',
2526 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '',
2527 esc_attr($disabled)
2528 );
2529
2530 if (!$this->is_activated) {
2531 echo '<div class="pro-feature-overlay">';
2532 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2533 echo '</div>';
2534 }
2535
2536 echo '</div>';
2537 }
2538
2539 public function mxchat_live_agent_message_bg_color_callback() {
2540 $disabled = $this->is_activated ? '' : 'disabled';
2541
2542 echo '<div class="pro-feature-wrapper">';
2543 printf(
2544 '<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 />',
2545 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '',
2546 esc_attr($disabled)
2547 );
2548
2549 if (!$this->is_activated) {
2550 echo '<div class="pro-feature-overlay">';
2551 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2552 echo '</div>';
2553 }
2554
2555 echo '</div>';
2556 }
2557
2558 public function mxchat_live_agent_message_font_color_callback() {
2559 $disabled = $this->is_activated ? '' : 'disabled';
2560
2561 echo '<div class="pro-feature-wrapper">';
2562 printf(
2563 '<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 />',
2564 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '',
2565 esc_attr($disabled)
2566 );
2567
2568 if (!$this->is_activated) {
2569 echo '<div class="pro-feature-overlay">';
2570 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2571 echo '</div>';
2572 }
2573
2574 echo '</div>';
2575 }
2576
2577 // Mode Indicator Background Color Callback
2578 public function mxchat_mode_indicator_bg_color_callback() {
2579 $disabled = $this->is_activated ? '' : 'disabled';
2580
2581 echo '<div class="pro-feature-wrapper">';
2582 printf(
2583 '<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 />',
2584 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '',
2585 esc_attr($disabled)
2586 );
2587
2588 if (!$this->is_activated) {
2589 echo '<div class="pro-feature-overlay">';
2590 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2591 echo '</div>';
2592 }
2593
2594 echo '</div>';
2595 }
2596
2597 // Mode Indicator Font Color Callback
2598 public function mxchat_mode_indicator_font_color_callback() {
2599 $disabled = $this->is_activated ? '' : 'disabled';
2600
2601 echo '<div class="pro-feature-wrapper">';
2602 printf(
2603 '<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 />',
2604 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '',
2605 esc_attr($disabled)
2606 );
2607
2608 if (!$this->is_activated) {
2609 echo '<div class="pro-feature-overlay">';
2610 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2611 echo '</div>';
2612 }
2613
2614 echo '</div>';
2615 }
2616
2617 public function mxchat_toolbar_icon_color_callback() {
2618 $disabled = $this->is_activated ? '' : 'disabled';
2619
2620 echo '<div class="pro-feature-wrapper">';
2621 printf(
2622 '<input type="text" id="toolbar_icon_color" name="mxchat_options[toolbar_icon_color]" value="%s" class="my-color-field" data-default-color="#212121" %s />',
2623 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '',
2624 esc_attr($disabled)
2625 );
2626
2627 if (!$this->is_activated) {
2628 echo '<div class="pro-feature-overlay">';
2629 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2630 echo '</div>';
2631 }
2632
2633 echo '</div>';
2634 }
2635
2636
2637
2638
2639 public function mxchat_top_bar_bg_color_callback() {
2640 $disabled = $this->is_activated ? '' : 'disabled';
2641
2642 echo '<div class="pro-feature-wrapper">';
2643 printf(
2644 '<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 />',
2645 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '',
2646 esc_attr($disabled)
2647 );
2648
2649 if (!$this->is_activated) {
2650 echo '<div class="pro-feature-overlay">';
2651 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2652 echo '</div>';
2653 }
2654
2655 echo '</div>';
2656 }
2657
2658 public function mxchat_send_button_font_color_callback() {
2659 $disabled = $this->is_activated ? '' : 'disabled';
2660
2661 echo '<div class="pro-feature-wrapper">';
2662 printf(
2663 '<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 />',
2664 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '',
2665 esc_attr($disabled)
2666 );
2667
2668 if (!$this->is_activated) {
2669 echo '<div class="pro-feature-overlay">';
2670 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2671 echo '</div>';
2672 }
2673
2674 echo '</div>';
2675 }
2676
2677 public function mxchat_chatbot_background_color_callback() {
2678 $disabled = $this->is_activated ? '' : 'disabled';
2679
2680 echo '<div class="pro-feature-wrapper">';
2681 printf(
2682 '<input type="text" id="chatbot_background_color" name="mxchat_options[chatbot_background_color]" value="%s" class="my-color-field" data-default-color="#000000" %s />',
2683 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '',
2684 esc_attr($disabled)
2685 );
2686
2687 if (!$this->is_activated) {
2688 echo '<div class="pro-feature-overlay">';
2689 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2690 echo '</div>';
2691 }
2692
2693 echo '</div>';
2694 }
2695
2696 public function mxchat_icon_color_callback() {
2697 $disabled = $this->is_activated ? '' : 'disabled';
2698
2699 echo '<div class="pro-feature-wrapper">';
2700 printf(
2701 '<input type="text" id="icon_color" name="mxchat_options[icon_color]" value="%s" class="my-color-field" data-default-color="#ffffff" %s />',
2702 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '',
2703 esc_attr($disabled)
2704 );
2705
2706 if (!$this->is_activated) {
2707 echo '<div class="pro-feature-overlay">';
2708 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2709 echo '</div>';
2710 }
2711
2712 echo '</div>';
2713 }
2714
2715 public function mxchat_custom_icon_callback() {
2716 $disabled = $this->is_activated ? '' : 'disabled';
2717 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
2718
2719 echo '<div class="pro-feature-wrapper">';
2720 printf(
2721 '<input type="url" id="custom_icon" name="mxchat_options[custom_icon]" value="%s" placeholder="Enter PNG URL" %s />',
2722 $custom_icon_url,
2723 esc_attr($disabled)
2724 );
2725 echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
2726
2727 if (!$this->is_activated) {
2728 echo '<div class="pro-feature-overlay">';
2729 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2730 echo '</div>';
2731 }
2732
2733 echo '</div>';
2734 }
2735
2736
2737 public function mxchat_chat_input_font_color_callback() {
2738 $disabled = $this->is_activated ? '' : 'disabled';
2739
2740 echo '<div class="pro-feature-wrapper">';
2741 printf(
2742 '<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 />',
2743 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '',
2744 esc_attr($disabled)
2745 );
2746
2747 if (!$this->is_activated) {
2748 echo '<div class="pro-feature-overlay">';
2749 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2750 echo '</div>';
2751 }
2752
2753 echo '</div>';
2754 }
2755
2756
2757 public function mxchat_append_to_body_callback() {
2758 $append_to_body_checked = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on' ? 'checked' : '';
2759 echo '<label class="toggle-switch">';
2760 printf(
2761 '<input type="checkbox" id="append_to_body" name="mxchat_options[append_to_body]" %s />',
2762 esc_attr($append_to_body_checked)
2763 );
2764 echo '<span class="slider"></span>';
2765 echo '</label>';
2766 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>';
2767 }
2768
2769
2770 public function mxchat_privacy_toggle_callback() {
2771 // Check if the privacy toggle is enabled
2772 $privacy_toggle_checked = isset($this->options['privacy_toggle']) && $this->options['privacy_toggle'] === 'on' ? 'checked' : '';
2773
2774 // Retrieve the stored privacy text if it exists
2775 $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>.';
2776
2777 // Output the toggle switch
2778 echo '<label class="toggle-switch">';
2779 printf(
2780 '<input type="checkbox" id="privacy_toggle" name="mxchat_options[privacy_toggle]" %s />',
2781 esc_attr($privacy_toggle_checked)
2782 );
2783 echo '<span class="slider"></span>';
2784 echo '</label>';
2785 echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
2786
2787 // Output the custom text input field
2788 printf(
2789 '<textarea id="privacy_text" name="mxchat_options[privacy_text]" rows="5" cols="50" class="regular-text">%s</textarea>',
2790 esc_textarea($privacy_text)
2791 );
2792 echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
2793 }
2794
2795
2796 public function mxchat_complianz_toggle_callback() {
2797 // Check if the Complianz toggle is enabled
2798 $complianz_toggle_checked = isset($this->options['complianz_toggle']) && $this->options['complianz_toggle'] === 'on' ? 'checked' : '';
2799
2800 // Check if the plugin is activated (paid feature)
2801 $disabled = $this->is_activated ? '' : 'disabled';
2802 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2803
2804 echo '<div class="' . esc_attr($class) . '">';
2805
2806 // Output the toggle switch
2807 echo '<label class="toggle-switch">';
2808 printf(
2809 '<input type="checkbox" id="complianz_toggle" name="mxchat_options[complianz_toggle]" %s %s />',
2810 esc_attr($complianz_toggle_checked),
2811 esc_attr($disabled)
2812 );
2813 echo '<span class="slider"></span>';
2814 echo '</label>';
2815 echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot.</p>';
2816
2817 // If the feature is not activated, show the Pro feature overlay
2818 if (!$this->is_activated) {
2819 echo '<div class="pro-feature-overlay">';
2820 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2821 echo '</div>';
2822 }
2823
2824 echo '</div>';
2825 }
2826
2827
2828
2829
2830
2831 public function mxchat_link_target_toggle_callback() {
2832 // Check if the toggle is enabled in the options
2833 $link_target_toggle = isset($this->options['link_target_toggle']) && $this->options['link_target_toggle'] === 'on' ? 'checked' : '';
2834
2835 // Output the toggle switch
2836 echo '<label class="toggle-switch">';
2837 printf(
2838 '<input type="checkbox" id="link_target_toggle" name="mxchat_options[link_target_toggle]" %s />',
2839 esc_attr($link_target_toggle)
2840 );
2841 echo '<span class="slider"></span>';
2842 echo '</label>';
2843 echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
2844 }
2845
2846 public function mxchat_chat_persistence_toggle_callback() {
2847 // Check if chat persistence toggle is enabled
2848 $chat_persistence_toggle_checked = isset($this->options['chat_persistence_toggle']) && $this->options['chat_persistence_toggle'] === 'on' ? 'checked' : '';
2849
2850 // Check if the plugin is activated (paid feature)
2851 $disabled = $this->is_activated ? '' : 'disabled';
2852 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2853
2854 echo '<div class="' . esc_attr($class) . '">';
2855
2856 // Output the toggle switch
2857 echo '<label class="toggle-switch">';
2858 printf(
2859 '<input type="checkbox" id="chat_persistence_toggle" name="mxchat_options[chat_persistence_toggle]" %s %s />',
2860 esc_attr($chat_persistence_toggle_checked),
2861 esc_attr($disabled)
2862 );
2863 echo '<span class="slider"></span>';
2864 echo '</label>';
2865 echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
2866
2867 // If not activated, show Pro feature overlay
2868 if (!$this->is_activated) {
2869 echo '<div class="pro-feature-overlay">';
2870 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2871 echo '</div>';
2872 }
2873
2874 echo '</div>';
2875 }
2876
2877
2878 public function mxchat_popular_question_1_callback() {
2879 printf(
2880 '<input type="text" id="popular_question_1" name="mxchat_options[popular_question_1]" value="%s" placeholder="Enter Popular Question 1" />',
2881 isset($this->options['popular_question_1']) ? esc_attr($this->options['popular_question_1']) : ''
2882 );
2883 echo '<p class="description">This will be the first popular question in the chatbot.</p>';
2884 }
2885
2886 public function mxchat_popular_question_2_callback() {
2887 // Check if the plugin is activated (paid feature)
2888 $disabled = $this->is_activated ? '' : 'disabled';
2889 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2890
2891 echo '<div class="' . esc_attr($class) . '">';
2892
2893 printf(
2894 '<input type="text" id="popular_question_2" name="mxchat_options[popular_question_2]" value="%s" placeholder="Enter Popular Question 2" %s />',
2895 isset($this->options['popular_question_2']) ? esc_attr($this->options['popular_question_2']) : '',
2896 esc_attr($disabled)
2897 );
2898 echo '<p class="description">This will be the second popular question in the chatbot.</p>';
2899
2900 // If not activated, show Pro feature overlay
2901 if (!$this->is_activated) {
2902 echo '<div class="pro-feature-overlay">';
2903 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2904 echo '</div>';
2905 }
2906
2907 echo '</div>';
2908 }
2909
2910 public function mxchat_popular_question_3_callback() {
2911 // Check if the plugin is activated (paid feature)
2912 $disabled = $this->is_activated ? '' : 'disabled';
2913 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2914
2915 echo '<div class="' . esc_attr($class) . '">';
2916
2917 printf(
2918 '<input type="text" id="popular_question_3" name="mxchat_options[popular_question_3]" value="%s" placeholder="Enter Popular Question 3" %s />',
2919 isset($this->options['popular_question_3']) ? esc_attr($this->options['popular_question_3']) : '',
2920 esc_attr($disabled)
2921 );
2922 echo '<p class="description">This will be the third popular question in the chatbot.</p>';
2923
2924 // If not activated, show Pro feature overlay
2925 if (!$this->is_activated) {
2926 echo '<div class="pro-feature-overlay">';
2927 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2928 echo '</div>';
2929 }
2930
2931 echo '</div>';
2932 }
2933
2934 public function mxchat_additional_popular_questions_callback() {
2935 $disabled = $this->is_activated ? '' : 'disabled';
2936 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
2937 $additional_questions = isset($this->options['additional_popular_questions']) ? $this->options['additional_popular_questions'] : array();
2938
2939 echo '<div class="' . esc_attr($class) . '">';
2940 echo '<div id="mxchat-additional-questions-container">';
2941
2942 if (!empty($additional_questions)) {
2943 foreach ($additional_questions as $index => $question) {
2944 printf(
2945 '<div class="mxchat-question-row">
2946 <input type="text" name="mxchat_options[additional_popular_questions][]" value="%s" placeholder="Enter Additional Popular Question %d" %s />
2947 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
2948 </div>',
2949 esc_attr($question),
2950 $index + 4, // Start numbering from 4
2951 esc_attr($disabled),
2952 esc_attr($disabled)
2953 );
2954 }
2955 } else {
2956 printf(
2957 '<div class="mxchat-question-row">
2958 <input type="text" name="mxchat_options[additional_popular_questions][]" value="" placeholder="Enter Additional Popular Question 4" %s />
2959 <button type="button" class="button mxchat-remove-question" aria-label="Remove question" %s>Remove</button>
2960 </div>',
2961 esc_attr($disabled),
2962 esc_attr($disabled)
2963 );
2964 }
2965
2966 echo '</div>';
2967
2968 printf(
2969 '<button type="button" class="button mxchat-add-question" aria-label="Add question" %s>Add Question</button>',
2970 esc_attr($disabled)
2971 );
2972
2973 echo '<p class="description">You can add more popular questions beyond the default three here.</p>';
2974
2975 if (!$this->is_activated) {
2976 echo '<div class="pro-feature-overlay">';
2977 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
2978 echo '</div>';
2979 }
2980
2981 echo '</div>';
2982 }
2983
2984
2985 /**
2986 * Callback for Brave API Key field.
2987 */
2988 public function mxchat_brave_api_key_callback() {
2989 $options = get_option('mxchat_options');
2990 $brave_api_key = isset($options['brave_api_key']) ? esc_attr($options['brave_api_key']) : '';
2991
2992 echo '<div class="api-key-wrapper">';
2993 printf(
2994 '<input type="password" id="brave_api_key" name="mxchat_options[brave_api_key]" value="%s" class="regular-text" />',
2995 $brave_api_key
2996 );
2997 echo '<button type="button" id="toggleBraveApiKeyVisibility" class="button-secondary">Show</button>';
2998 echo '</div>';
2999 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
3000
3001 ?>
3002 <?php
3003 }
3004
3005 /**
3006 * Callback for Number of Images to Return field.
3007 */
3008 public function mxchat_brave_image_count_callback() {
3009 $options = get_option('mxchat_options');
3010 $brave_image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
3011
3012 printf(
3013 '<input type="number" id="brave_image_count" name="mxchat_options[brave_image_count]" value="%d" min="1" max="6" />',
3014 $brave_image_count
3015 );
3016 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
3017 }
3018
3019 /**
3020 * Callback for Safe Search field.
3021 */
3022 public function mxchat_brave_safe_search_callback() {
3023 $options = get_option('mxchat_options');
3024 $brave_safe_search = isset($options['brave_safe_search']) ? esc_attr($options['brave_safe_search']) : 'strict';
3025
3026 ?>
3027 <select id="brave_safe_search" name="mxchat_options[brave_safe_search]">
3028 <option value="strict" <?php selected($brave_safe_search, 'strict'); ?>><?php _e('Strict', 'mxchat'); ?></option>
3029 <option value="off" <?php selected($brave_safe_search, 'off'); ?>><?php _e('Off', 'mxchat'); ?></option>
3030 </select>
3031 <p class="description"><?php _e('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat'); ?></p>
3032 <?php
3033 }
3034
3035
3036 /**
3037 * Callback for Number of News Articles field.
3038 */
3039 public function mxchat_brave_news_count_callback() {
3040 $options = get_option('mxchat_options');
3041 $brave_news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
3042
3043 printf(
3044 '<input type="number" id="brave_news_count" name="mxchat_options[brave_news_count]" value="%d" min="1" max="10" />',
3045 $brave_news_count
3046 );
3047 echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
3048 }
3049
3050 /**
3051 * Callback for Country field.
3052 */
3053 public function mxchat_brave_country_callback() {
3054 $options = get_option('mxchat_options');
3055 $brave_country = isset($options['brave_country']) ? esc_attr($options['brave_country']) : 'us';
3056
3057 printf(
3058 '<input type="text" id="brave_country" name="mxchat_options[brave_country]" value="%s" maxlength="2" class="small-text" />',
3059 $brave_country
3060 );
3061 echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
3062 }
3063
3064 /**
3065 * Callback for Language field.
3066 */
3067 public function mxchat_brave_language_callback() {
3068 $options = get_option('mxchat_options');
3069 $brave_language = isset($options['brave_language']) ? esc_attr($options['brave_language']) : 'en';
3070
3071 printf(
3072 '<input type="text" id="brave_language" name="mxchat_options[brave_language]" value="%s" maxlength="2" class="small-text" />',
3073 $brave_language
3074 );
3075 echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
3076 }
3077
3078
3079
3080
3081
3082 // Section Callback
3083 public function mxchat_pdf_intent_section_callback() {
3084 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
3085 }
3086
3087 public function mxchat_chat_toolbar_toggle_callback() {
3088 $checked = isset($this->options['chat_toolbar_toggle']) && $this->options['chat_toolbar_toggle'] === 'on' ? 'checked' : '';
3089 $disabled = $this->is_activated ? '' : 'disabled';
3090 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3091
3092 echo '<div class="' . esc_attr($class) . '">';
3093 echo '<label class="toggle-switch">';
3094 printf(
3095 '<input type="checkbox" id="chat_toolbar_toggle" name="mxchat_options[chat_toolbar_toggle]" value="on" %s %s>',
3096 esc_attr($checked),
3097 esc_attr($disabled)
3098 );
3099 echo '<span class="slider"></span>';
3100 echo '</label>';
3101
3102 if (!$this->is_activated) {
3103 echo '<div class="pro-feature-overlay">';
3104 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3105 echo '</div>';
3106 }
3107
3108 echo '</div>';
3109 echo '<p class="description">Enable to display the chat toolbar (default is hidden).</p>';
3110 }
3111
3112
3113
3114 // Intent Trigger Text Callback
3115 public function mxchat_pdf_intent_trigger_text_callback() {
3116 $disabled = $this->is_activated ? '' : 'disabled';
3117 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3118
3119 echo '<div class="' . esc_attr($class) . '">';
3120 printf(
3121 '<textarea id="pdf_intent_trigger_text" name="mxchat_options[pdf_intent_trigger_text]" rows="3" cols="50" placeholder="Enter trigger text" %s>%s</textarea>',
3122 esc_attr($disabled),
3123 isset($this->options['pdf_intent_trigger_text']) ? esc_textarea($this->options['pdf_intent_trigger_text']) : ''
3124 );
3125 echo '<p class="description">Text displayed when the intent is triggered.</p>';
3126
3127 if (!$this->is_activated) {
3128 echo '<div class="pro-feature-overlay">';
3129 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3130 echo '</div>';
3131 }
3132 echo '</div>';
3133 }
3134
3135 // Success Text Callback
3136 public function mxchat_pdf_intent_success_text_callback() {
3137 $disabled = $this->is_activated ? '' : 'disabled';
3138 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3139
3140 echo '<div class="' . esc_attr($class) . '">';
3141 printf(
3142 '<textarea id="pdf_intent_success_text" name="mxchat_options[pdf_intent_success_text]" rows="3" cols="50" placeholder="Enter success text" %s>%s</textarea>',
3143 esc_attr($disabled),
3144 isset($this->options['pdf_intent_success_text']) ? esc_textarea($this->options['pdf_intent_success_text']) : ''
3145 );
3146 echo '<p class="description">Text displayed when the intent is successful.</p>';
3147
3148 if (!$this->is_activated) {
3149 echo '<div class="pro-feature-overlay">';
3150 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3151 echo '</div>';
3152 }
3153 echo '</div>';
3154 }
3155
3156 // Error Text Callback
3157 public function mxchat_pdf_intent_error_text_callback() {
3158 $disabled = $this->is_activated ? '' : 'disabled';
3159 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3160
3161 echo '<div class="' . esc_attr($class) . '">';
3162 printf(
3163 '<textarea id="pdf_intent_error_text" name="mxchat_options[pdf_intent_error_text]" rows="3" cols="50" placeholder="Enter error text" %s>%s</textarea>',
3164 esc_attr($disabled),
3165 isset($this->options['pdf_intent_error_text']) ? esc_textarea($this->options['pdf_intent_error_text']) : ''
3166 );
3167 echo '<p class="description">Text displayed when an error occurs during the intent.</p>';
3168
3169 if (!$this->is_activated) {
3170 echo '<div class="pro-feature-overlay">';
3171 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3172 echo '</div>';
3173 }
3174 echo '</div>';
3175 }
3176
3177 // Maximum PDF Pages Callback
3178 public function mxchat_pdf_max_pages_callback() {
3179 $disabled = $this->is_activated ? '' : 'disabled';
3180 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3181 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
3182
3183 echo '<div class="' . esc_attr($class) . '">';
3184 printf(
3185 '<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;" />',
3186 esc_attr($max_pages),
3187 esc_attr($disabled)
3188 );
3189 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
3190 echo '<p class="description">Set the maximum number of PDF pages users can upload for processing. (1-69 pages)</p>';
3191
3192 if (!$this->is_activated) {
3193 echo '<div class="pro-feature-overlay">';
3194 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3195 echo '</div>';
3196 }
3197 echo '</div>';
3198 }
3199
3200
3201 // Status Toggle Callback
3202 public function mxchat_live_agent_status_callback() {
3203 $disabled = $this->is_activated ? '' : 'disabled';
3204 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3205 $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'offline';
3206
3207 echo '<div class="' . esc_attr($class) . '">';
3208 echo '<label class="mxchat-switch">';
3209 printf(
3210 '<input type="checkbox" id="live_agent_status" name="mxchat_options[live_agent_status]" value="online" %s %s />',
3211 checked($status, 'online', false),
3212 esc_attr($disabled)
3213 );
3214 echo '<span class="mxchat-slider"></span>';
3215 echo '</label>';
3216 echo '<label for="live_agent_status" class="mxchat-status-label">';
3217 echo '<span class="status-text">' . ($status === 'online' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
3218 echo '</label>';
3219
3220 if (!$this->is_activated) {
3221 echo '<div class="pro-feature-overlay">';
3222 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3223 echo '</div>';
3224 }
3225 echo '</div>';
3226 }
3227
3228 // Away Message Callback
3229 public function mxchat_live_agent_away_message_callback() {
3230 $disabled = $this->is_activated ? '' : 'disabled';
3231 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3232 $message = isset($this->options['live_agent_away_message'])
3233 ? $this->options['live_agent_away_message']
3234 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
3235
3236 echo '<div class="' . esc_attr($class) . '">';
3237 printf(
3238 '<textarea id="live_agent_away_message" name="mxchat_options[live_agent_away_message]" rows="3" cols="50" %s>%s</textarea>',
3239 esc_attr($disabled),
3240 esc_textarea($message)
3241 );
3242 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
3243
3244 if (!$this->is_activated) {
3245 echo '<div class="pro-feature-overlay">';
3246 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3247 echo '</div>';
3248 }
3249 echo '</div>';
3250 }
3251 public function mxchat_live_agent_notification_message_callback() {
3252 $disabled = $this->is_activated ? '' : 'disabled';
3253 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3254 $message = isset($this->options['live_agent_notification_message'])
3255 ? $this->options['live_agent_notification_message']
3256 : __('Live agent has been notified.', 'mxchat');
3257
3258 echo '<div class="' . esc_attr($class) . '">';
3259 printf(
3260 '<textarea id="live_agent_notification_message" name="mxchat_options[live_agent_notification_message]" rows="3" cols="50" %s>%s</textarea>',
3261 esc_attr($disabled),
3262 esc_textarea($message)
3263 );
3264 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
3265
3266 if (!$this->is_activated) {
3267 echo '<div class="pro-feature-overlay">';
3268 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3269 echo '</div>';
3270 }
3271 echo '</div>';
3272 }
3273
3274 public function mxchat_live_agent_webhook_url_callback() {
3275 $disabled = $this->is_activated ? '' : 'disabled';
3276 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3277 $webhook_url = isset($this->options['live_agent_webhook_url'])
3278 ? esc_url($this->options['live_agent_webhook_url'])
3279 : esc_url(get_option('live_agent_webhook_url', ''));
3280
3281 echo '<div class="' . esc_attr($class) . '">';
3282 printf(
3283 '<input type="password" id="live_agent_webhook_url" name="mxchat_options[live_agent_webhook_url]" value="%s" class="regular-text" %s />',
3284 $webhook_url,
3285 esc_attr($disabled)
3286 );
3287 echo '<button type="button" id="toggleWebhookUrlVisibility">Show</button>';
3288 echo '<p class="description">Enter your Slack webhook URL for live agent notifications.</p>';
3289 if (!$this->is_activated) {
3290 echo '<div class="pro-feature-overlay">';
3291 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3292 echo '</div>';
3293 }
3294 echo '</div>';
3295 }
3296
3297
3298 public function mxchat_similarity_threshold_callback() {
3299 $threshold = get_option('mxchat_similarity_threshold', 80); // Default to 80
3300 ?>
3301 <input type="range" id="mxchat_similarity_threshold" name="mxchat_similarity_threshold"
3302 min="70" max="85" step="1"
3303 value="<?php echo esc_attr($threshold); ?>"
3304 oninput="document.getElementById('mxchat_threshold_value').textContent = this.value;">
3305 <span id="mxchat_threshold_value"><?php echo esc_html($threshold); ?></span>
3306 <p class="description">
3307 Lower values match less relevant content, higher values match more strictly.
3308 </p>
3309 <?php
3310 }
3311
3312
3313
3314 public function mxchat_live_agent_secret_key_callback() {
3315 $disabled = $this->is_activated ? '' : 'disabled';
3316 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3317
3318 echo '<div class="' . esc_attr($class) . '">';
3319 printf(
3320 '<input type="password" id="live_agent_secret_key" name="mxchat_options[live_agent_secret_key]" value="%s" class="regular-text" %s />',
3321 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
3322 esc_attr($disabled)
3323 );
3324 echo '<button type="button" id="toggleSecretKeyVisibility">Show</button>';
3325 echo '<p class="description">Secret key for validating Slack requests. Keep this secure.</p>';
3326
3327 if (!$this->is_activated) {
3328 echo '<div class="pro-feature-overlay">';
3329 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3330 echo '</div>';
3331 }
3332 echo '</div>';
3333 }
3334
3335 public function mxchat_live_agent_bot_token_callback() {
3336 $disabled = $this->is_activated ? '' : 'disabled';
3337 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3338
3339 echo '<div class="' . esc_attr($class) . '">';
3340 printf(
3341 '<input type="password" id="live_agent_bot_token" name="mxchat_options[live_agent_bot_token]" value="%s" class="regular-text" %s />',
3342 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
3343 esc_attr($disabled)
3344 );
3345 echo '<button type="button" id="toggleBotTokenVisibility">Show</button>';
3346 echo '<p class="description">Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.</p>';
3347
3348 if (!$this->is_activated) {
3349 echo '<div class="pro-feature-overlay">';
3350 echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3351 echo '</div>';
3352 }
3353 echo '</div>';
3354 }
3355
3356 public function mxchat_enqueue_admin_assets() {
3357 wp_enqueue_style('wp-color-picker');
3358
3359 // Get the plugin version or file modification time for cache busting
3360 $plugin_version = '1.5.4'; // Replace this with your plugin's version
3361
3362 // File paths
3363 $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
3364 $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
3365 $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
3366 $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
3367
3368 // Check if files exist and get modification times
3369 $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
3370 $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
3371 $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
3372 $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
3373
3374 // Enqueue scripts and styles with corrected paths
3375 wp_enqueue_script(
3376 'mxchat-color-picker',
3377 plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
3378 array('wp-color-picker'),
3379 $color_picker_version,
3380 true
3381 );
3382
3383 wp_enqueue_script(
3384 'mxchat-embedding-check',
3385 plugin_dir_url(__FILE__) . '../js/embedding-check.js',
3386 array(),
3387 $embedding_check_version,
3388 true
3389 );
3390
3391 wp_enqueue_script(
3392 'mxchat-transcripts-js',
3393 plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
3394 array('jquery'),
3395 $transcripts_js_version,
3396 true
3397 );
3398
3399 wp_enqueue_script(
3400 'mxchat-admin-js',
3401 plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
3402 array('jquery'),
3403 $plugin_version,
3404 true
3405 );
3406
3407
3408
3409 // Localize the script with data for the activation process
3410 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
3411 'ajax_url' => admin_url('admin-ajax.php'),
3412 'nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
3413 ));
3414
3415 wp_enqueue_style(
3416 'mxchat-admin-css',
3417 plugin_dir_url(__FILE__) . '../css/admin-style.css',
3418 array(),
3419 $admin_css_version
3420 );
3421
3422 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
3423 'ajax_url' => admin_url('admin-ajax.php'),
3424 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
3425 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
3426 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
3427 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
3428 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
3429 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
3430 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
3431 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
3432 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
3433 'icon_color' => $this->options['icon_color'] ?? '#fff',
3434 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
3435 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
3436 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
3437
3438 // New fields for Loops Integration
3439 'loops_api_key' => $this->options['loops_api_key'] ?? '',
3440 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
3441 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
3442 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.',
3443 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.",
3444 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?",
3445 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
3446 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
3447
3448 // Live Agent Integration
3449 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
3450 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
3451 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
3452
3453 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
3454 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
3455 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
3456 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
3457 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
3458 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
3459
3460 ));
3461
3462 // Localize the script to pass the nonce and other data to JavaScript
3463 wp_localize_script('mxchat-admin-js', 'mxchatInlineEdit', array(
3464 'nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
3465 'ajax_url' => admin_url('admin-ajax.php')
3466 ));
3467
3468
3469 }
3470
3471 public function mxchat_sanitize($input) {
3472 $new_input = array();
3473
3474 if (isset($input['api_key'])) {
3475 $new_input['api_key'] = sanitize_text_field($input['api_key']);
3476 }
3477
3478 if (isset($input['similarity_threshold'])) {
3479 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
3480 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
3481 }
3482
3483 if (isset($input['xai_api_key'])) {
3484 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
3485 }
3486
3487 if (isset($input['claude_api_key'])) {
3488 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
3489 }
3490
3491 if (isset($input['enable_woocommerce_integration'])) {
3492 $new_input['enable_woocommerce_integration'] = isset($input['enable_woocommerce_integration']) && $input['enable_woocommerce_integration'] === '1' ? '1' : '0';
3493
3494 }
3495
3496 if (isset($input['privacy_toggle'])) {
3497 $new_input['privacy_toggle'] = $input['privacy_toggle'];
3498 }
3499
3500 if (isset($input['complianz_toggle'])) {
3501 $new_input['complianz_toggle'] = $input['complianz_toggle'];
3502 }
3503
3504 // Handle custom privacy text input
3505 if (isset($input['privacy_text'])) {
3506 // Allow basic HTML for links
3507 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
3508 }
3509
3510 if (isset($input['system_prompt_instructions'])) {
3511 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
3512 }
3513
3514 if (isset($input['mxchat_pro_email'])) {
3515 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
3516 }
3517
3518 if (isset($input['mxchat_activation_key'])) {
3519 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
3520 }
3521
3522 if (isset($input['append_to_body'])) {
3523 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
3524 }
3525
3526 if (isset($input['top_bar_title'])) {
3527 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
3528 }
3529
3530 if (isset($input['intro_message'])) {
3531 $new_input['intro_message'] = sanitize_text_field($input['intro_message']);
3532 }
3533
3534 if (isset($input['input_copy'])) {
3535 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
3536 }
3537
3538
3539 if (isset($input['rate_limit_message'])) {
3540 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
3541 }
3542
3543 if (isset($input['rate_limit_logged_in'])) {
3544 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3545 $rate_limit = sanitize_text_field($input['rate_limit_logged_in']);
3546
3547 if (in_array($rate_limit, $allowed_limits, true)) {
3548 $new_input['rate_limit_logged_in'] = $rate_limit;
3549 } else {
3550 $new_input['rate_limit_logged_in'] = '100'; // Default for logged-in users
3551 }
3552 }
3553
3554 if (isset($input['rate_limit_logged_out'])) {
3555 $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
3556 $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
3557
3558 if (in_array($rate_limit, $allowed_limits, true)) {
3559 $new_input['rate_limit_logged_out'] = $rate_limit;
3560 } else {
3561 $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
3562 }
3563 }
3564
3565 if (isset($input['pre_chat_message'])) {
3566 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
3567 }
3568
3569 if (isset($input['model'])) {
3570 $allowed_models = array(
3571 'grok-beta',
3572 'claude-3-5-sonnet-20241022',
3573 'claude-3-opus-20240229',
3574 'claude-3-sonnet-20240229',
3575 'claude-3-haiku-20240307',
3576 'gpt-4o',
3577 'gpt-4o-mini',
3578 'gpt-4-turbo',
3579 'gpt-4',
3580 'gpt-3.5-turbo',
3581 );
3582 if (in_array($input['model'], $allowed_models)) {
3583 $new_input['model'] = sanitize_text_field($input['model']);
3584 }
3585 }
3586
3587 // Sanitize new pro features
3588 if (isset($input['close_button_color'])) {
3589 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
3590 }
3591
3592 if (isset($input['chatbot_bg_color'])) {
3593 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
3594 }
3595
3596 if (isset($input['woocommerce_consumer_key'])) {
3597 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
3598 }
3599
3600 if (isset($input['woocommerce_consumer_secret'])) {
3601 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
3602 }
3603
3604 if (isset($input['user_message_bg_color'])) {
3605 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
3606 }
3607
3608 if (isset($input['user_message_font_color'])) {
3609 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
3610 }
3611
3612 if (isset($input['bot_message_bg_color'])) {
3613 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
3614 }
3615
3616 if (isset($input['bot_message_font_color'])) {
3617 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
3618 }
3619
3620 if (isset($input['live_agent_message_bg_color'])) {
3621 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
3622 }
3623
3624 if (isset($input['live_agent_message_font_color'])) {
3625 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
3626 }
3627
3628 if (isset($input['mode_indicator_bg_color'])) {
3629 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
3630 }
3631
3632 if (isset($input['mode_indicator_font_color'])) {
3633 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
3634 }
3635
3636 if (isset($input['toolbar_icon_color'])) {
3637 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
3638 }
3639
3640 if (isset($input['top_bar_bg_color'])) {
3641 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
3642 }
3643
3644 if (isset($input['send_button_font_color'])) {
3645 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
3646 }
3647
3648 if (isset($input['chatbot_background_color'])) {
3649 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
3650 }
3651
3652 if (isset($input['icon_color'])) {
3653 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
3654 }
3655
3656 if (isset($input['custom_icon'])) {
3657 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
3658 }
3659
3660 if (isset($input['chat_input_font_color'])) {
3661 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
3662 }
3663
3664 // Sanitize link_target_toggle
3665 if (isset($input['link_target_toggle'])) {
3666 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
3667 }
3668
3669 // Sanitize Loops API Key
3670 if (isset($input['loops_api_key'])) {
3671 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
3672 }
3673
3674 if (isset($input['chat_persistence_toggle'])) {
3675 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
3676 }
3677
3678
3679
3680 if (isset($input['popular_question_1'])) {
3681 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
3682 }
3683
3684 if (isset($input['popular_question_2'])) {
3685 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
3686 }
3687
3688 if (isset($input['popular_question_3'])) {
3689 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
3690 }
3691
3692 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
3693 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
3694 }
3695
3696 // Sanitize Loops Mailing List
3697 if (isset($input['loops_mailing_list'])) {
3698 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
3699 }
3700
3701 // Sanitize Triggered Phrase Response
3702 if (isset($input['triggered_phrase_response'])) {
3703 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
3704 }
3705
3706 if (isset($input['email_capture_response'])) {
3707 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
3708 }
3709
3710
3711 // Sanitize Brave Search Settings
3712 if (isset($input['brave_api_key'])) {
3713 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
3714 }
3715
3716 if (isset($input['brave_image_count'])) {
3717 $image_count = intval($input['brave_image_count']);
3718 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
3719 }
3720
3721 if (isset($input['brave_safe_search'])) {
3722 $allowed = array('strict', 'off');
3723 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
3724 }
3725
3726 if (isset($input['brave_news_count'])) {
3727 $news_count = intval($input['brave_news_count']);
3728 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
3729 }
3730
3731 if (isset($input['brave_country'])) {
3732 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
3733 }
3734
3735 if (isset($input['brave_language'])) {
3736 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
3737 }
3738
3739
3740 if (isset($input['chat_toolbar_toggle'])) {
3741 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
3742 }
3743
3744
3745 if (isset($input['pdf_intent_trigger_text'])) {
3746 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
3747 }
3748
3749 if (isset($input['pdf_intent_success_text'])) {
3750 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
3751 }
3752
3753 if (isset($input['pdf_intent_error_text'])) {
3754 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
3755 }
3756
3757 if (isset($input['pdf_max_pages'])) {
3758 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
3759 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
3760 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
3761 }
3762 }
3763
3764
3765 if (isset($input['live_agent_webhook_url'])) {
3766 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
3767 }
3768 if (isset($input['live_agent_secret_key'])) {
3769 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
3770 }
3771
3772 // Live Agent Integration
3773 if (isset($input['live_agent_bot_token'])) {
3774 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
3775 }
3776
3777 if (isset($input['live_agent_status'])) {
3778 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'online') ? 'online' : 'offline';
3779 }
3780 if (isset($input['live_agent_away_message'])) {
3781 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
3782 }
3783 if (isset($input['live_agent_notification_message'])) {
3784 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
3785 }
3786
3787
3788 return $new_input;
3789 }
3790
3791
3792 // Method to append the chatbot to the body
3793 public function mxchat_append_chatbot_to_body() {
3794 $options = get_option('mxchat_options');
3795 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
3796 echo do_shortcode('[mxchat_chatbot floating="yes"]');
3797 }
3798 }
3799
3800
3801
3802 private static function mxchat_extract_main_content($html) {
3803 $dom = new DOMDocument;
3804 libxml_use_internal_errors(true); // Suppress HTML parsing errors
3805 @$dom->loadHTML($html);
3806 libxml_clear_errors();
3807
3808 $xpath = new DOMXPath($dom);
3809
3810 // Simplified selectors focusing on common content areas
3811 $selectors = [
3812 '//article',
3813 '//*[@id="content"]',
3814 '//*[@class="entry-content"]',
3815 '//main',
3816 ];
3817
3818 foreach ($selectors as $selector) {
3819 $nodes = $xpath->query($selector);
3820 if ($nodes->length > 0) {
3821 $content = '';
3822 foreach ($nodes as $node) {
3823 $content .= $dom->saveHTML($node);
3824 }
3825 return $content;
3826 }
3827 }
3828
3829 // Fallback: Return the entire body content if no specific selector matches
3830 $body = $dom->getElementsByTagName('body');
3831 return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
3832 }
3833
3834 public function mxchat_handle_sitemap_submission() {
3835 // Check if the form was submitted and verify permissions
3836 if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
3837 wp_die(esc_html__('Unauthorized access', 'mxchat'));
3838 }
3839
3840 // Verify nonce
3841 check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
3842
3843 // Validate URL
3844 if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
3845 set_transient('mxchat_admin_notice_error',
3846 esc_html__('Please provide a valid URL.', 'mxchat'),
3847 30
3848 );
3849 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3850 exit;
3851 }
3852
3853 $submitted_url = esc_url_raw($_POST['sitemap_url']);
3854 $response = wp_remote_get($submitted_url, array('timeout' => 30));
3855
3856 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
3857 $error_message = is_wp_error($response) ? $response->get_error_message() : 'Failed to fetch URL';
3858 set_transient('mxchat_admin_notice_error',
3859 sprintf(
3860 esc_html__('Failed to fetch the URL: %s', 'mxchat'),
3861 esc_html($error_message)
3862 ),
3863 30
3864 );
3865 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3866 exit;
3867 }
3868
3869 $content_type = wp_remote_retrieve_header($response, 'content-type');
3870 $body_content = wp_remote_retrieve_body($response);
3871
3872 if (empty($body_content)) {
3873 set_transient('mxchat_admin_notice_error',
3874 esc_html__('Empty response received from URL.', 'mxchat'),
3875 30
3876 );
3877 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3878 exit;
3879 }
3880
3881 // Handle PDF URL
3882 if ($this->is_pdf_url($submitted_url, $response)) {
3883 $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
3884
3885 if ($result === 'scheduled') {
3886 set_transient(
3887 'mxchat_last_pdf_url',
3888 sanitize_text_field($submitted_url),
3889 DAY_IN_SECONDS
3890 );
3891 set_transient('mxchat_admin_notice_info',
3892 esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
3893 30
3894 );
3895 } else {
3896 set_transient('mxchat_admin_notice_error',
3897 esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
3898 30
3899 );
3900 }
3901
3902 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3903 exit;
3904 }
3905
3906 // Handle Sitemap XML
3907 if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
3908 libxml_use_internal_errors(true);
3909 $xml = simplexml_load_string($body_content);
3910 $xml_errors = libxml_get_errors();
3911 libxml_clear_errors();
3912
3913 if ($xml === false || !empty($xml_errors)) {
3914 set_transient('mxchat_admin_notice_error',
3915 esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
3916 30
3917 );
3918 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3919 exit;
3920 }
3921
3922 $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
3923
3924 if ($result === 'scheduled') {
3925 set_transient(
3926 'mxchat_last_sitemap_url',
3927 sanitize_text_field($submitted_url),
3928 DAY_IN_SECONDS
3929 );
3930 set_transient('mxchat_admin_notice_info',
3931 esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
3932 30
3933 );
3934 } else {
3935 set_transient('mxchat_admin_notice_error',
3936 esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
3937 30
3938 );
3939 }
3940
3941 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3942 exit;
3943 }
3944
3945 // Handle Regular URL
3946 $page_content = $this->mxchat_extract_main_content($body_content);
3947 $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
3948
3949 if (empty($sanitized_content)) {
3950 set_transient('mxchat_admin_notice_error',
3951 esc_html__('No valid content found on the provided URL.', 'mxchat'),
3952 30
3953 );
3954 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3955 exit;
3956 }
3957
3958 $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
3959 if (is_array($embedding_vector)) {
3960 MxChat_Utils::submit_content_to_db(
3961 $sanitized_content,
3962 $submitted_url,
3963 $this->options['api_key']
3964 );
3965 set_transient('mxchat_admin_notice_success',
3966 esc_html__('URL content successfully submitted!', 'mxchat'),
3967 30
3968 );
3969 } else {
3970 set_transient('mxchat_admin_notice_error',
3971 esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
3972 30
3973 );
3974 }
3975
3976 wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
3977 exit;
3978 }
3979 private function is_pdf_url($url, $response) {
3980 $content_type = wp_remote_retrieve_header($response, 'content-type');
3981 $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
3982
3983 return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
3984 }
3985
3986 private function handle_pdf_for_knowledge_base($pdf_url, $response) {
3987 if (!current_user_can('manage_options')) {
3988 //error_log('Unauthorized PDF processing attempt');
3989 return false;
3990 }
3991
3992 $pdf_url = esc_url_raw($pdf_url);
3993 $upload_dir = wp_upload_dir();
3994
3995 if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
3996 //error_log(sprintf('Upload directory error: %s', esc_html($upload_dir['error'])));
3997 return false;
3998 }
3999
4000 $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
4001 $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
4002
4003 $response_body = wp_remote_retrieve_body($response);
4004 if (empty($response_body)) {
4005 //error_log('Empty PDF response body');
4006 return false;
4007 }
4008
4009 if (!wp_mkdir_p(dirname($pdf_path))) {
4010 //error_log(sprintf('Failed to create directory for PDF: %s', esc_html($pdf_path)));
4011 return false;
4012 }
4013
4014 try {
4015 file_put_contents($pdf_path, $response_body);
4016
4017 if (!file_exists($pdf_path)) {
4018 throw new Exception('Failed to save PDF file');
4019 }
4020
4021 $parser = new \Smalot\PdfParser\Parser();
4022 $pdf = $parser->parseFile($pdf_path);
4023 $total_pages = absint(count($pdf->getPages()));
4024
4025 if ($total_pages < 1) {
4026 throw new Exception('Invalid PDF: no pages found');
4027 }
4028
4029 wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
4030 'pdf_path' => $pdf_path,
4031 'pdf_url' => $pdf_url,
4032 'total_pages' => $total_pages,
4033 'batch_size' => absint(15),
4034 'batch_pause' => absint(10)
4035 ));
4036
4037 $status_data = array(
4038 'total_pages' => $total_pages,
4039 'processed_pages' => 0,
4040 'status' => 'processing',
4041 'last_update' => time()
4042 );
4043
4044 set_transient(
4045 sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
4046 array_map('sanitize_text_field', $status_data),
4047 DAY_IN_SECONDS
4048 );
4049
4050 return 'scheduled';
4051
4052 } catch (Exception $e) {
4053 //error_log(sprintf('Error preparing PDF for processing: %s', esc_html($e->getMessage())));
4054 if (file_exists($pdf_path)) {
4055 wp_delete_file($pdf_path);
4056 }
4057 return false;
4058 }
4059 }
4060 public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
4061 // Validate inputs
4062 $pdf_path = sanitize_text_field($pdf_path);
4063 $pdf_url = esc_url_raw($pdf_url);
4064 $total_pages = absint($total_pages);
4065 $batch_size = absint($batch_size);
4066 $batch_pause = absint($batch_pause);
4067
4068 try {
4069 if (!file_exists($pdf_path)) {
4070 throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
4071 }
4072
4073 $parser = new \Smalot\PdfParser\Parser();
4074 $pdf = $parser->parseFile($pdf_path);
4075 $pages = $pdf->getPages();
4076
4077 // Get current progress
4078 $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
4079 $status = get_transient($status_key);
4080
4081 if (!$status || !is_array($status)) {
4082 throw new Exception('Invalid status data retrieved from transient');
4083 }
4084
4085 $start_page = absint($status['processed_pages']);
4086 $end_page = min($start_page + $batch_size, $total_pages);
4087
4088 $instance = new self(); // Create an instance of the class
4089
4090 for ($i = $start_page; $i < $end_page; $i++) {
4091 $text = $pages[$i]->getText();
4092 $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
4093
4094 if (!empty($sanitized_content)) {
4095 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4096 if (is_array($embedding_vector)) {
4097 $metadata = array(
4098 'document_type' => 'pdf',
4099 'total_pages' => $total_pages,
4100 'current_page' => $i + 1,
4101 'prev_page' => $i > 0 ? $i : null,
4102 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
4103 'source_url' => $pdf_url
4104 );
4105
4106 $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
4107 $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
4108
4109 $options = get_option('mxchat_options');
4110 MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
4111 }
4112 }
4113
4114 // Update progress with sanitized data
4115 $status['processed_pages'] = absint($i + 1);
4116 $status['last_update'] = time();
4117 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4118 }
4119
4120 // Schedule next batch if needed
4121 if ($end_page < $total_pages) {
4122 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
4123 'pdf_path' => $pdf_path,
4124 'pdf_url' => $pdf_url,
4125 'total_pages' => $total_pages,
4126 'batch_size' => $batch_size,
4127 'batch_pause' => $batch_pause
4128 ));
4129 } else {
4130 // Processing complete
4131 $status['status'] = 'complete';
4132 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4133 if (file_exists($pdf_path)) {
4134 wp_delete_file($pdf_path);
4135 }
4136 }
4137
4138 } catch (\Exception $e) {
4139 $status['status'] = 'error';
4140 $status['error'] = sanitize_text_field($e->getMessage());
4141 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4142 if (file_exists($pdf_path)) {
4143 wp_delete_file($pdf_path);
4144 }
4145 }
4146 }
4147
4148 public function get_pdf_processing_status($pdf_url) {
4149 $pdf_url = esc_url_raw($pdf_url);
4150 $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
4151
4152 if (!$status || !is_array($status)) {
4153 return false;
4154 }
4155
4156 return array(
4157 'total_pages' => absint($status['total_pages']),
4158 'processed_pages' => absint($status['processed_pages']),
4159 'percentage' => round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100),
4160 'status' => sanitize_text_field($status['status']),
4161 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4162 );
4163 }
4164
4165 private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
4166 if (!current_user_can('manage_options')) {
4167 //error_log('Unauthorized sitemap processing attempt');
4168 return false;
4169 }
4170
4171 try {
4172 $sitemap_url = esc_url_raw($sitemap_url);
4173
4174 if (!$xml || !is_object($xml)) {
4175 throw new Exception('Invalid XML object provided');
4176 }
4177
4178 $urls = [];
4179 foreach ($xml->url as $url_element) {
4180 $url = esc_url_raw((string)$url_element->loc);
4181 if ($url) {
4182 $urls[] = $url;
4183 }
4184 }
4185
4186 $total_urls = absint(count($urls));
4187
4188 if ($total_urls < 1) {
4189 throw new Exception('No valid URLs found in sitemap');
4190 }
4191
4192 wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
4193 'urls' => $urls,
4194 'sitemap_url' => $sitemap_url,
4195 'total_urls' => $total_urls,
4196 'batch_size' => absint(10),
4197 'batch_pause' => absint(5)
4198 ));
4199
4200 $status_data = array(
4201 'total_urls' => $total_urls,
4202 'processed_urls' => 0,
4203 'status' => 'processing',
4204 'last_update' => time()
4205 );
4206
4207 set_transient(
4208 sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
4209 array_map('sanitize_text_field', $status_data),
4210 DAY_IN_SECONDS
4211 );
4212
4213 return 'scheduled';
4214
4215 } catch (\Exception $e) {
4216 //error_log(sprintf('Error preparing sitemap for processing: %s', esc_html($e->getMessage())));
4217 return false;
4218 }
4219 }
4220 public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
4221 // Validate inputs
4222 $sitemap_url = esc_url_raw($sitemap_url);
4223 $total_urls = absint($total_urls);
4224 $batch_size = absint($batch_size);
4225 $batch_pause = absint($batch_pause);
4226
4227 if (!is_array($urls) || empty($urls)) {
4228 return;
4229 }
4230
4231 try {
4232 $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
4233 $status = get_transient($status_key);
4234
4235 if (!$status || !is_array($status)) {
4236 throw new Exception('Invalid status data retrieved from transient');
4237 }
4238
4239 $start_url = absint($status['processed_urls']);
4240 $end_url = min($start_url + $batch_size, $total_urls);
4241
4242 $instance = new self(); // Create an instance of the class
4243 $embedding_success = true;
4244
4245 for ($i = $start_url; $i < $end_url; $i++) {
4246 $page_url = esc_url_raw($urls[$i]);
4247 $page_response = wp_remote_get($page_url);
4248
4249 if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
4250 continue;
4251 }
4252
4253 $page_html = wp_remote_retrieve_body($page_response);
4254 $page_content = $instance->mxchat_extract_main_content($page_html); // Call via instance
4255 $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content); // Call via instance
4256
4257 if (!empty($sanitized_content)) {
4258 $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
4259 if (is_array($embedding_vector)) {
4260 $options = get_option('mxchat_options');
4261 MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
4262 } else {
4263 $embedding_success = false;
4264 }
4265 }
4266
4267 $status['processed_urls'] = absint($i + 1);
4268 $status['last_update'] = time();
4269 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4270 }
4271
4272 if ($end_url < $total_urls) {
4273 wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
4274 'urls' => array_map('esc_url_raw', $urls),
4275 'sitemap_url' => $sitemap_url,
4276 'total_urls' => $total_urls,
4277 'batch_size' => $batch_size,
4278 'batch_pause' => $batch_pause,
4279 ));
4280 } else {
4281 $status['status'] = 'complete';
4282 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4283 }
4284 } catch (\Exception $e) {
4285 $status['status'] = 'error';
4286 $status['error'] = sanitize_text_field($e->getMessage());
4287 set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
4288 }
4289 }
4290
4291 public function get_sitemap_processing_status($sitemap_url) {
4292 $sitemap_url = esc_url_raw($sitemap_url);
4293 $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
4294
4295 if (!$status || !is_array($status)) {
4296 return false;
4297 }
4298
4299 return array(
4300 'total_urls' => absint($status['total_urls']),
4301 'processed_urls' => absint($status['processed_urls']),
4302 'percentage' => round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100),
4303 'status' => sanitize_text_field($status['status']),
4304 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
4305 );
4306 }
4307 public function mxchat_stop_processing() {
4308 // Verify permissions
4309 if (!current_user_can('manage_options')) {
4310 wp_die(esc_html__('Unauthorized access', 'mxchat'));
4311 }
4312
4313 // Verify nonce
4314 check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
4315
4316 // Get the last sitemap URL and clear its transient
4317 $sitemap_url = get_transient('mxchat_last_sitemap_url');
4318 if ($sitemap_url) {
4319 delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
4320 delete_transient('mxchat_last_sitemap_url');
4321 }
4322
4323 // Get the last PDF URL and clear its transient
4324 $pdf_url = get_transient('mxchat_last_pdf_url');
4325 if ($pdf_url) {
4326 delete_transient('mxchat_pdf_status_' . md5($pdf_url));
4327 delete_transient('mxchat_last_pdf_url');
4328 }
4329
4330 // Unschedule any pending sitemap events
4331 $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
4332 if ($timestamp) {
4333 wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
4334 }
4335
4336 // Redirect back with a success message
4337 set_transient('mxchat_admin_notice_success',
4338 esc_html__('Processing has been stopped successfully.', 'mxchat'),
4339 30
4340 );
4341 wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
4342 exit;
4343 }
4344 private function mxchat_sanitize_content_for_api($content) {
4345 // Remove script, style tags, and HTML comments
4346 $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
4347 $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
4348 $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
4349
4350 // Remove all HTML tags and decode HTML entities
4351 $content = wp_strip_all_tags($content);
4352 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
4353
4354 // Trim and normalize whitespace
4355 $content = trim(preg_replace('/\s+/', ' ', $content));
4356
4357 return $content;
4358 }
4359
4360
4361
4362 private function mxchat_fetch_loops_mailing_lists($api_key) {
4363 $url = 'https://app.loops.so/api/v1/lists';
4364 $response = wp_remote_get($url, array(
4365 'headers' => array(
4366 'Authorization' => 'Bearer ' . $api_key,
4367 'Content-Type' => 'application/json'
4368 )
4369 ));
4370
4371 if (is_wp_error($response)) {
4372 return array();
4373 }
4374
4375 $body = wp_remote_retrieve_body($response);
4376 $lists = json_decode($body, true);
4377
4378 return isset($lists) && is_array($lists) ? $lists : array();
4379 }
4380
4381
4382
4383
4384
4385 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
4386 if (empty($vec1) || empty($vec2)) {
4387 return 0.0;
4388 }
4389
4390 $dot_product = 0.0;
4391 $norm_a = 0.0;
4392 $norm_b = 0.0;
4393
4394 for ($i = 0; $i < count($vec1); $i++) {
4395 $dot_product += $vec1[$i] * $vec2[$i];
4396 $norm_a += pow($vec1[$i], 2);
4397 $norm_b += pow($vec2[$i], 2);
4398 }
4399
4400 if ($norm_a == 0.0 || $norm_b == 0.0) {
4401 return 0.0;
4402 } else {
4403 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
4404 }
4405 }
4406
4407
4408
4409
4410 }
4411 ?>
4412