PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.6.0
MxChat – AI Chatbot & Content Generation for WordPress v1.6.0
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
← All changes | includes/class-mxchat-admin.php +5488 -6133 2.0.61.6.0 View file →
@@ -1,6133 +1,5488 @@
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_init', array($this, 'mxchat_prompts_page_init'));
25 - add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 - add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
27 - add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
28 - add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 - add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 - add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 - add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
32 - add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 - add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 - add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 - add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
36 - add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
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 - add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
40 - add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
41 - add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
42 - add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
43 - add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
44 - add_action('save_post', array($this, 'handle_post_update'), 10, 3);
45 - add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
46 - add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
47 - add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
48 - add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
49 - add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
50 - add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
51 - if (isset($this->options['enable_woocommerce_integration']) &&
52 - ($this->options['enable_woocommerce_integration'] === '1' ||
53 - $this->options['enable_woocommerce_integration'] === 'on')) {
54 -
55 - //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
56 - add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
57 - add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
58 - add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
59 - }
60 - }
61 -
62 - // Method to check if the license is active
63 -private function is_license_active() {
64 - $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
65 - return $license_status === esc_html__('active', 'mxchat');
66 - }
67 -
68 - // Initialize default options
69 -private function initialize_default_options() {
70 - $default_options = array(
71 - 'api_key' => '',
72 - 'xai_api_key' => '',
73 - 'claude_api_key' => '',
74 - 'deepseek_api_key' => '',
75 - 'voyage_api_key' => '',
76 - 'embedding_model' => 'text-embedding-ada-002',
77 - 'system_prompt_instructions' => esc_html__('[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:
78 - - Your name is [Chatbot Name].
79 - - 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.
80 - - Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs.
81 - - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
82 - - 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.', 'mxchat'),
83 - 'model' => esc_html__('gpt-4o', 'mxchat'),
84 - 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
85 - 'role_rate_limits' => array(),
86 - 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
87 - 'enable_email_block' => '',
88 - 'email_blocker_header_content' => esc_html__("<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>", 'mxchat'),
89 - 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
90 - 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
91 - 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
92 - 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
93 - 'append_to_body' => esc_html__('off', 'mxchat'),
94 - 'close_button_color' => esc_html__('#fff', 'mxchat'),
95 - 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
96 - 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
97 - 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
98 - 'bot_message_bg_color' => esc_html__('#212121', 'mxchat'),
99 - 'bot_message_font_color' => esc_html__('#fff', 'mxchat'),
100 - 'top_bar_bg_color' => esc_html__('#212121', 'mxchat'),
101 - 'send_button_font_color' => esc_html__('#212121', 'mxchat'),
102 - 'chat_input_font_color' => esc_html__('#212121', 'mxchat'),
103 - 'chatbot_background_color' => esc_html__('#212121', 'mxchat'),
104 - 'icon_color' => esc_html__('#fff', 'mxchat'),
105 - 'enable_woocommerce_integration' => esc_html__('0', 'mxchat'),
106 - 'link_target_toggle' => esc_html__('off', 'mxchat'),
107 - 'pre_chat_message' => esc_html__('Hey there! Ask me anything!', 'mxchat'),
108 -
109 - // New fields for Loops Integration
110 - 'loops_api_key' => '',
111 - 'loops_mailing_list' => '',
112 - 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
113 - 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
114 - 'popular_question_1' => '',
115 - 'popular_question_2' => '',
116 - 'popular_question_3' => '',
117 - 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
118 - 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
119 - 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
120 - 'pdf_max_pages' => 69,
121 -
122 - // Live Agent Integration
123 - 'live_agent_webhook_url' => '',
124 - 'live_agent_secret_key' => '',
125 - 'live_agent_bot_token' => '',
126 - 'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'),
127 - 'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'),
128 - 'chat_toolbar_toggle' => esc_html__('off', 'mxchat'),
129 - 'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'),
130 - 'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'),
131 - 'toolbar_icon_color' => esc_html__('#212121', 'mxchat'),
132 - );
133 -
134 -
135 - // Merge existing options with defaults
136 - $existing_options = get_option('mxchat_options', array());
137 - $merged_options = wp_parse_args($existing_options, $default_options);
138 -
139 - // Update the options if they have changed
140 - if ($existing_options !== $merged_options) {
141 - update_option('mxchat_options', $merged_options);
142 - }
143 -
144 - // Add default limits for each role
145 - $roles = wp_roles()->get_names();
146 - foreach ($roles as $role_id => $role_name) {
147 - $default_options['role_rate_limits'][$role_id] = esc_html__('100', 'mxchat');
148 - }
149 -
150 - return $default_options;
151 -
152 - // Update the $this->options property
153 - $this->options = $merged_options;
154 - }
155 -
156 -
157 -
158 -public function mxchat_add_plugin_page() {
159 - // Main menu page
160 - add_menu_page(
161 - esc_html__('MxChat Settings', 'mxchat'),
162 - esc_html__('MxChat', 'mxchat'),
163 - 'manage_options',
164 - 'mxchat-max',
165 - array($this, 'mxchat_create_admin_page'),
166 - 'dashicons-testimonial',
167 - 6
168 - );
169 -
170 - // Submenu page for Knowledge
171 - add_submenu_page(
172 - 'mxchat-max',
173 - esc_html__('Prompts', 'mxchat'),
174 - esc_html__('Knowledge', 'mxchat'),
175 - 'manage_options',
176 - 'mxchat-prompts',
177 - array($this, 'mxchat_create_prompts_page')
178 - );
179 -
180 - add_submenu_page(
181 - 'mxchat-max',
182 - esc_html__('Chat Transcripts', 'mxchat'),
183 - esc_html__('Transcripts', 'mxchat'),
184 - 'manage_options',
185 - 'mxchat-transcripts',
186 - array($this, 'mxchat_create_transcripts_page')
187 - );
188 -
189 - add_submenu_page(
190 - 'mxchat-max',
191 - esc_html__('MxChat Intents', 'mxchat'),
192 - esc_html__('Intents', 'mxchat'),
193 - 'manage_options',
194 - 'mxchat-intents',
195 - array($this, 'mxchat_intents_page_html')
196 - );
197 -
198 - add_submenu_page(
199 - 'mxchat-max',
200 - esc_html__('Add Ons', 'mxchat'),
201 - esc_html__('Add Ons', 'mxchat'),
202 - 'manage_options',
203 - 'mxchat-addons',
204 - array($this, 'mxchat_create_addons_page')
205 - );
206 -
207 - // Submenu page for Activation Key
208 - add_submenu_page(
209 - 'mxchat-max',
210 - esc_html__('Pro Upgrade', 'mxchat'),
211 - esc_html__('Pro Upgrade', 'mxchat'),
212 - 'manage_options',
213 - 'mxchat-activation',
214 - array($this, 'mxchat_create_activation_page')
215 - );
216 -}
217 -
218 -public function mxchat_create_addons_page() {
219 - require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php';
220 - $addons_page = new MxChat_Addons();
221 - $addons_page->render_page();
222 -}
223 -
224 -public function mxchat_save_setting_callback() {
225 - check_ajax_referer('mxchat_save_setting_nonce');
226 - if (!current_user_can('manage_options')) {
227 - wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
228 - }
229 - $name = isset($_POST['name']) ? $_POST['name'] : '';
230 - // Strip slashes from the value before saving
231 - $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
232 - if (empty($name)) {
233 - wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
234 - }
235 - // Load the full options array
236 - $options = get_option('mxchat_options', []);
237 - // Handle special cases
238 - switch ($name) {
239 - case 'additional_popular_questions':
240 - $questions = json_decode($value, true); // No need for stripslashes here
241 - if (is_array($questions)) {
242 - $options[$name] = $questions;
243 - // Also update old option for backwards compatibility
244 - update_option('additional_popular_questions', $questions);
245 - }
246 - break;
247 - case 'user_message_bg_color':
248 - case 'user_message_font_color':
249 - case 'bot_message_bg_color':
250 - case 'bot_message_font_color':
251 - case 'top_bar_bg_color':
252 - case 'send_button_font_color':
253 - case 'chatbot_background_color':
254 - case 'icon_color':
255 - case 'chat_input_font_color':
256 - case 'live_agent_message_bg_color':
257 - case 'live_agent_message_font_color':
258 - case 'mode_indicator_bg_color':
259 - case 'mode_indicator_font_color':
260 - case 'toolbar_icon_color':
261 - // Store color values directly
262 - $options[$name] = $value;
263 - break;
264 - case 'live_agent_status':
265 - // Set the new value
266 - $options[$name] = ($value === 'on') ? 'on' : 'off';
267 - break;
268 - case 'enable_woocommerce_integration':
269 - // Handle values that used to be 1/0
270 - $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
271 - break;
272 - default:
273 - // Handle role rate limits
274 - if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
275 - // Extract role ID from the name
276 - preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
277 - if (isset($matches[1])) {
278 - $role_id = $matches[1];
279 - // Initialize role_rate_limits if it doesn't exist
280 - if (!isset($options['role_rate_limits'])) {
281 - $options['role_rate_limits'] = [];
282 - }
283 - // Update the specific role's rate limit
284 - $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
285 - break;
286 - }
287 - }
288 - // Handle toggles
289 - if (strpos($name, 'toggle') !== false || in_array($name, [
290 - 'chat_persistence_toggle',
291 - 'privacy_toggle',
292 - 'complianz_toggle',
293 - 'chat_toolbar_toggle' // Removed live_agent_status from here
294 - ])) {
295 - $options[$name] = ($value === 'on') ? 'on' : 'off';
296 - } else {
297 - // Store all other values directly
298 - $options[$name] = $value;
299 - }
300 - break;
301 - }
302 - // Save all updates
303 - $updated = update_option('mxchat_options', $options);
304 - // Handle backwards compatibility for certain fields
305 - $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
306 - 'brave_country', 'brave_language', 'similarity_threshold'];
307 - if (in_array($name, $legacy_fields)) {
308 - // Also update the individual option for backwards compatibility
309 - update_option($name, $value);
310 - }
311 - if ($updated) {
312 - wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
313 - } else {
314 - wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
315 - }
316 -}
317 -
318 -/**
319 - * Handles AJAX auto-save for prompts and auto-sync settings.
320 - */
321 -public function mxchat_save_prompts_setting_callback() {
322 - // Check the correct nonce for this action.
323 - check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
324 -
325 - if ( ! current_user_can( 'manage_options' ) ) {
326 - wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
327 - }
328 -
329 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
330 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
331 -
332 - if ( empty( $name ) ) {
333 - wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
334 - }
335 -
336 - // Handle standalone auto-sync settings.
337 - if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
338 - // Convert checkbox value to "1" if checked, "0" if not.
339 - $value = ( $value === 'on' ) ? '1' : '0';
340 - update_option( $name, $value );
341 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
342 - }
343 -
344 - // Handle fields stored in the 'mxchat_prompts_options' array.
345 -// Handle fields stored in the 'mxchat_prompts_options' array.
346 -if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
347 - // Extract the key name using regex.
348 - if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
349 - $key = sanitize_text_field( $matches[1] );
350 - $options = get_option( 'mxchat_prompts_options', [] );
351 -
352 - // Remove Pinecone-specific settings from the main plugin handling.
353 - if ( 'mxchat_use_pinecone' === $key ) {
354 - // Optionally, you could simply ignore this field or set a default value.
355 - // For example, ensure it's always 0 or remove it entirely:
356 - // $options[ $key ] = '0';
357 - // Or, if you prefer, just return an error so that the main plugin doesn't handle it:
358 - wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
359 - } else {
360 - // For all other fields, use the existing logic.
361 - $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
362 - }
363 - update_option( 'mxchat_prompts_options', $options );
364 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
365 - }
366 -}
367 -
368 -
369 - wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
370 -}
371 -
372 -public function mxchat_display_admin_notice() {
373 - // Success notice
374 - if ($message = get_transient('mxchat_admin_notice_success')) {
375 - ?>
376 - <div class="notice notice-success is-dismissible">
377 - <p><?php echo esc_html($message); ?></p>
378 - <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
379 - </div>
380 - <?php
381 - delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
382 - }
383 -
384 - // Error notice
385 - if ($message = get_transient('mxchat_admin_notice_error')) {
386 - ?>
387 - <div class="notice notice-error is-dismissible">
388 - <p><?php echo esc_html($message); ?></p>
389 - <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
390 - </div>
391 - <?php
392 - delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
393 - }
394 -}
395 -
396 -
397 -
398 -
399 -
400 -public function mxchat_create_admin_page() {
401 -
402 - ?>
403 - <div class="wrap mxchat-admin">
404 - <?php if (!$this->is_activated): ?>
405 - <div class="mxchat-pro-banner">
406 - <p>
407 - <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
408 - <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
409 - </p>
410 - </div>
411 - <?php endif; ?>
412 -
413 - <div class="mxchat-agents-banner">
414 - <p>
415 - <strong><?php echo esc_html__('The MxChat Add-Ons Platform', 'mxchat'); ?></strong><?php echo esc_html__(' is here! Pro users get unlimited access to our growing collection of powerful add-ons. ', 'mxchat'); ?><a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>"><?php echo esc_html__('Explore all add-ons', 'mxchat'); ?></a><?php echo esc_html__(' including Forms Builder, Theme Customizer, Content Moderation, and more – all included with your lifetime Pro license!', 'mxchat'); ?>
416 - </p>
417 - </div>
418 -
419 - <h2 class="mxchat-nav-tab-wrapper">
420 - <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
421 - <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
422 - <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
423 - </h2>
424 -
425 - <div id="chatbot" class="mxchat-tab-content active">
426 - <div class="mxchat-autosave-section">
427 - <?php do_settings_sections('mxchat-chatbot'); ?>
428 - </div>
429 - </div>
430 -
431 - <div id="embed" class="mxchat-tab-content">
432 - <div class="mxchat-autosave-section">
433 -
434 - <div class="mxchat-settings-section">
435 - <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
436 - <table class="form-table">
437 - <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
438 - </table>
439 - </div>
440 -
441 - <div class="section-divider"></div>
442 -
443 - <div class="mxchat-settings-section">
444 - <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
445 - <table class="form-table">
446 - <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
447 - </table>
448 - </div>
449 -
450 - <div class="section-divider"></div>
451 -
452 - <div class="mxchat-settings-section">
453 - <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
454 - <table class="form-table">
455 - <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
456 - </table>
457 - </div>
458 -
459 - <div class="section-divider"></div>
460 -
461 - <!-- Live Agent Settings Section -->
462 - <div class="mxchat-settings-section">
463 - <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
464 - <p><?php echo esc_html__('Visit our', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#slack_integration" target="_blank"><?php echo esc_html__('documentation page', 'mxchat'); ?></a> <?php echo esc_html__('to set up live agent transfer via Slack.', 'mxchat'); ?></p>
465 - <table class="form-table">
466 - <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
467 - </table>
468 - </div>
469 - </div>
470 - </div>
471 -
472 -
473 - <div id="general" class="mxchat-tab-content">
474 - <?php do_settings_sections('mxchat-general'); ?>
475 - <p>
476 - <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
477 - <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
478 - <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
479 - </p>
480 - <p>
481 - <?php echo esc_html__('If you like our plugin, please consider', 'mxchat'); ?> <a href="https://wordpress.org/plugins/mxchat-basic/#reviews" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('leaving us a review', 'mxchat'); ?></a>.
482 - </p>
483 -
484 - <div class="faq-item">
485 - <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
486 - <p>
487 - <?php echo esc_html__('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.', 'mxchat'); ?>
488 - </p>
489 - <p>
490 - <?php echo esc_html__('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.', 'mxchat'); ?>
491 - </p>
492 - <p>
493 - <?php echo esc_html__('You can obtain your Claude API key by signing up on the', 'mxchat'); ?> <a href="https://www.anthropic.com/api" target="_blank" rel="noopener"><?php echo esc_html__('Anthropic API page', 'mxchat'); ?></a>.
494 - </p>
495 - </div>
496 -
497 - <div class="faq-item">
498 - <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
499 - <p>
500 - <?php echo esc_html__('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.', 'mxchat'); ?>
501 - </p>
502 - <p>
503 - <?php echo esc_html__('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', 'mxchat'); ?> <a href="https://docs.x.ai/docs" target="_blank" rel="noopener"><?php echo esc_html__('X.AI API documentation', 'mxchat'); ?></a>.
504 - </p>
505 - </div>
506 -
507 - <div class="faq-item">
508 - <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
509 - <p>
510 - <?php echo esc_html__('Yes, you will need an OpenAI API key to power the chatbot. You can obtain an API key by signing up on the', 'mxchat'); ?> <a href="https://platform.openai.com/signup" target="_blank" rel="noopener"><?php echo esc_html__('OpenAI platform', 'mxchat'); ?></a>. <?php echo esc_html__('After signing up, you must add credits to your account—typically, $5 in credits is sufficient to get started.', 'mxchat'); ?>
511 - </p>
512 - <p>
513 - <?php echo esc_html__('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.', 'mxchat'); ?>
514 - </p>
515 - </div>
516 -
517 - <div class="faq-item">
518 - <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
519 - <p>
520 - <?php echo esc_html__('You can add the chatbot using the shortcode', 'mxchat'); ?> <code>[mxchat_chatbot floating="yes"]</code> <?php echo esc_html__('or', 'mxchat'); ?> <code>[mxchat_chatbot floating="no"]</code>. <?php echo esc_html__('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.', 'mxchat'); ?>
521 - </p>
522 - </div>
523 -
524 - <div class="faq-item">
525 - <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
526 - <p>
527 - <?php echo esc_html__('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.', 'mxchat'); ?>
528 - </p>
529 - </div>
530 -
531 - <div class="faq-item">
532 - <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
533 - <p>
534 - <?php echo esc_html__('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.', 'mxchat'); ?>
535 - </p>
536 - </div>
537 -
538 - <div class="faq-item">
539 - <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
540 - <p>
541 - <?php echo esc_html__('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', 'mxchat'); ?> <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Intents Documentation', 'mxchat'); ?></a>.
542 - </p>
543 - </div>
544 -
545 - <div class="faq-item">
546 - <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
547 - <p>
548 - <?php echo esc_html__('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.', 'mxchat'); ?>
549 - </p>
550 - </div>
551 -
552 - <div class="faq-item">
553 - <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
554 - <p>
555 - <?php echo esc_html__('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.', 'mxchat'); ?>
556 - </p>
557 - </div>
558 -
559 - <div class="faq-item">
560 - <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
561 - <p>
562 - <?php echo esc_html__('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', 'mxchat'); ?> <a href="https://mxchat.ai/contact/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('contact us', 'mxchat'); ?></a> <?php echo esc_html__('for personalized support.', 'mxchat'); ?>
563 - </p>
564 - <p>
565 - <?php echo esc_html__('We’re dedicated to your success and ready to guide you in aligning the AI\'s behavior to meet your goals.', 'mxchat'); ?>
566 - </p>
567 - </div>
568 -
569 - <div class="faq-item">
570 - <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
571 - <p>
572 - <?php echo esc_html__('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', 'mxchat'); ?> <a href="https://loops.so" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('Loops website', 'mxchat'); ?></a> <?php echo esc_html__('to get started or to sign up for an account.', 'mxchat'); ?>
573 - </p>
574 - </div>
575 -
576 - </div>
577 - </div>
578 - <?php
579 -}
580 -
581 -
582 -public function mxchat_create_transcripts_page() {
583 - global $wpdb;
584 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
585 -
586 - // Get basic stats
587 - $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
588 - $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
589 -
590 - // Count unique users with detailed breakdown
591 - $total_users = $wpdb->get_var("
592 - SELECT COUNT(DISTINCT
593 - CASE
594 - WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
595 - WHEN user_id != 0 THEN CONCAT('user_', user_id)
596 - WHEN user_identifier NOT LIKE 'Tech-Savvy User'
597 - AND user_identifier NOT LIKE 'Detail-Oriented User'
598 - AND user_identifier NOT LIKE 'Language Learner'
599 - AND user_identifier NOT LIKE 'Casual Browser'
600 - AND user_identifier NOT LIKE 'Policy Enforcer'
601 - AND user_identifier NOT LIKE 'Researcher'
602 - AND user_identifier NOT LIKE 'Loyalty Member'
603 - AND user_identifier NOT LIKE 'Gift Buyer'
604 - AND user_identifier NOT LIKE 'Parent or Caregiver'
605 - THEN user_identifier
606 - ELSE session_id
607 - END
608 - )
609 - FROM $table_name
610 - WHERE role != 'assistant'
611 - ");
612 -
613 - // Get user type breakdown
614 - $registered_users = $wpdb->get_var("
615 - SELECT COUNT(DISTINCT user_email)
616 - FROM $table_name
617 - WHERE user_email != '' AND user_email IS NOT NULL
618 - ");
619 -
620 - $guest_users = $wpdb->get_var("
621 - SELECT COUNT(DISTINCT user_identifier)
622 - FROM $table_name
623 - WHERE (user_email = '' OR user_email IS NULL)
624 - AND role != 'assistant'
625 - AND user_identifier NOT LIKE 'Tech-Savvy User'
626 - AND user_identifier NOT LIKE 'Detail-Oriented User'
627 - AND user_identifier NOT LIKE 'Language Learner'
628 - AND user_identifier NOT LIKE 'Casual Browser'
629 - AND user_identifier NOT LIKE 'Policy Enforcer'
630 - AND user_identifier NOT LIKE 'Researcher'
631 - AND user_identifier NOT LIKE 'Loyalty Member'
632 - AND user_identifier NOT LIKE 'Gift Buyer'
633 - AND user_identifier NOT LIKE 'Parent or Caregiver'
634 - ");
635 -
636 - // Get agent test messages count
637 - $agent_tests = $wpdb->get_var("
638 - SELECT COUNT(DISTINCT session_id)
639 - FROM $table_name
640 - WHERE user_identifier IN (
641 - 'Tech-Savvy User',
642 - 'Detail-Oriented User',
643 - 'Language Learner',
644 - 'Casual Browser',
645 - 'Policy Enforcer',
646 - 'Researcher',
647 - 'Loyalty Member',
648 - 'Gift Buyer',
649 - 'Parent or Caregiver'
650 - )
651 - ");
652 - ?>
653 - <div class="wrap mxchat-transcripts-wrapper">
654 - <!-- Hero Section -->
655 - <div class="mxchat-transcripts-hero">
656 - <h1 class="mxchat-main-title">
657 - Chat <span class="mxchat-gradient-text">Transcripts</span>
658 - </h1>
659 - <p class="mxchat-hero-subtitle">
660 - <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
661 - </p>
662 - </div>
663 - <div class="mxchat-content">
664 - <!-- Stats Cards -->
665 - <div class="mxchat-stats-grid">
666 - <div class="mxchat-stat-card">
667 - <div class="stat-icon">💬</div>
668 - <div class="stat-content">
669 - <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
670 - <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
671 - </div>
672 - </div>
673 - <div class="mxchat-stat-card">
674 - <div class="stat-icon">📝</div>
675 - <div class="stat-content">
676 - <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
677 - <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
678 - </div>
679 - </div>
680 - <div class="mxchat-stat-card">
681 - <div class="stat-icon">👥</div>
682 - <div class="stat-content">
683 - <span class="stat-value"><?php echo esc_html($total_users); ?></span>
684 - <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
685 - <span class="stat-sublabel">
686 - <?php
687 - echo sprintf(
688 - esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
689 - $registered_users,
690 - $guest_users,
691 - $agent_tests
692 - );
693 - ?>
694 - </span>
695 - </div>
696 - </div>
697 - </div>
698 - <!-- Search and Filter Controls -->
699 - <div class="mxchat-controls-wrapper">
700 - <div class="mxchat-search-box">
701 - <input type="text" id="mxchat-search-transcripts"
702 - placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
703 - class="regular-text">
704 - </div>
705 - <form id="mxchat-delete-form" method="post">
706 - <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
707 - <div class="mxchat-controls">
708 - <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
709 - <span class="dashicons dashicons-download"></span>
710 - <?php esc_html_e('Export All Chats', 'mxchat'); ?>
711 - </button>
712 - <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
713 - <span class="dashicons dashicons-yes-alt"></span>
714 - <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
715 - </button>
716 - <button type="submit" class="button delete-chats-button">
717 - <span class="dashicons dashicons-trash"></span>
718 - <?php esc_html_e('Delete Selected', 'mxchat'); ?>
719 - </button>
720 - </div>
721 - </form>
722 - </div>
723 - <!-- Transcripts Container -->
724 - <div id="mxchat-transcripts"></div>
725 - </div>
726 - </div>
727 - <?php
728 -}
729 -
730 -public function export_chat_transcripts() {
731 - if (!current_user_can('manage_options')) {
732 - wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
733 - }
734 -
735 - check_ajax_referer('mxchat_export_transcripts', 'security');
736 -
737 - global $wpdb;
738 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
739 -
740 - // Get all transcripts ordered by session and timestamp
741 - $results = $wpdb->get_results(
742 - "SELECT session_id, user_email, user_identifier, role, message, timestamp
743 - FROM {$table_name}
744 - ORDER BY session_id, timestamp ASC"
745 - );
746 -
747 - if (empty($results)) {
748 - wp_send_json_error(array('message' => 'No transcripts found.'));
749 - wp_die();
750 - }
751 -
752 - // Set headers for CSV download
753 - header('Content-Type: text/csv');
754 - header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
755 - header('Pragma: no-cache');
756 - header('Expires: 0');
757 -
758 - // Create output stream
759 - $output = fopen('php://output', 'w');
760 -
761 - // Add UTF-8 BOM for proper Excel encoding
762 - fputs($output, "\xEF\xBB\xBF");
763 -
764 - // Add CSV headers
765 - fputcsv($output, array(
766 - 'Session ID',
767 - 'Email',
768 - 'User Identifier',
769 - 'Role',
770 - 'Message',
771 - 'Timestamp'
772 - ));
773 -
774 - // Add data rows
775 - foreach ($results as $row) {
776 - fputcsv($output, array(
777 - $row->session_id,
778 - $row->user_email,
779 - $row->user_identifier,
780 - $row->role,
781 - $row->message,
782 - $row->timestamp
783 - ));
784 - }
785 -
786 - fclose($output);
787 - wp_die();
788 -}
789 -
790 -public function mxchat_fetch_chat_history() {
791 - global $wpdb;
792 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
793 -
794 - if (!current_user_can('manage_options')) {
795 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
796 - }
797 -
798 - // First get unique session IDs ordered by most recent message in each session
799 - $session_ids = $wpdb->get_col(
800 - $wpdb->prepare(
801 - "SELECT DISTINCT session_id
802 - FROM {$table_name}
803 - GROUP BY session_id
804 - ORDER BY MAX(timestamp) DESC"
805 - )
806 - );
807 -
808 - if (empty($session_ids)) {
809 - wp_die(esc_html__('No chat history available.', 'mxchat'));
810 - }
811 -
812 - ob_start();
813 - echo '<div class="mxchat-transcript">';
814 -
815 - // Iterate through sessions from newest to oldest
816 - foreach ($session_ids as $session_id) {
817 - // Get the email associated with this session (if available)
818 - $email = $wpdb->get_var(
819 - $wpdb->prepare(
820 - "SELECT user_email
821 - FROM {$table_name}
822 - WHERE session_id = %s AND user_email != ''
823 - ORDER BY timestamp ASC
824 - LIMIT 1",
825 - $session_id
826 - )
827 - );
828 -
829 - // Debug: Check email retrieval
830 - if (empty($email)) {
831 - //error_log("No email found for session {$session_id}");
832 - } else {
833 - //error_log("Email for session {$session_id}: " . $email);
834 - }
835 -
836 - // Get messages for this session ordered by timestamp
837 - $messages = $wpdb->get_results(
838 - $wpdb->prepare(
839 - "SELECT * FROM {$table_name}
840 - WHERE session_id = %s
841 - ORDER BY timestamp ASC",
842 - $session_id
843 - )
844 - );
845 -
846 - // Start session block
847 - echo '<div class="mxchat-session">';
848 - echo '<div class="mxchat-session-header">';
849 - // Wrap checkbox and session ID in one block
850 - echo '<div class="mxchat-session-id">';
851 - echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
852 - echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
853 - echo '</div>';
854 -
855 - // Place email directly below the session ID block
856 - if (!empty($email)) {
857 - echo '<div class="mxchat-session-email">';
858 - echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
859 - echo '</div>';
860 - }
861 - echo '</div>';
862 -
863 - echo '<div class="mxchat-messages">';
864 -
865 - // Display messages for this session
866 - foreach ($messages as $transcript) {
867 - $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
868 -
869 - // Determine message styling
870 - switch ($transcript->role) {
871 - case 'assistant':
872 - case 'bot':
873 - $message_class = 'bot-message';
874 - $display_role = esc_html__('Chatbot', 'mxchat');
875 - break;
876 - case 'user':
877 - $message_class = 'user-message';
878 - $display_role = !empty($transcript->user_identifier)
879 - ? sanitize_text_field($transcript->user_identifier)
880 - : esc_html__('User', 'mxchat');
881 - break;
882 - case 'agent':
883 - $message_class = 'agent-message';
884 - $display_role = esc_html__('Agent', 'mxchat');
885 - break;
886 - default:
887 - $message_class = 'unknown-message';
888 - $display_role = esc_html__('Unknown', 'mxchat');
889 - }
890 -
891 - // Process message content
892 - $message_content = wp_kses(
893 - stripslashes($transcript->message),
894 - [
895 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
896 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
897 - 'a' => ['href' => [], 'title' => []]
898 - ]
899 - );
900 - $message_content = nl2br($message_content);
901 -
902 - // Render message
903 - echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
904 - echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
905 - echo '<div class="mxchat-message-content">' . $message_content . '</div>';
906 - echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
907 - echo '</div>';
908 - }
909 -
910 - // Close session block
911 - echo '</div></div>';
912 - }
913 -
914 - echo '</div>';
915 - $output = ob_get_clean();
916 - echo $output;
917 - wp_die();
918 -}
919 -
920 -
921 -
922 -public function mxchat_create_prompts_page() {
923 - global $wpdb;
924 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
925 -
926 - // Display success message if all prompts were deleted
927 - if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
928 - echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
929 - }
930 -
931 - // Set up pagination and search query
932 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
933 - $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
934 - $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
935 - $per_page = 10;
936 - $offset = ($current_page - 1) * $per_page;
937 -
938 - // Modify query to handle search input
939 - $sql_search = "";
940 - if ($search_query) {
941 - $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
942 - }
943 -
944 - // Retrieve total number of prompts, considering search filter
945 - $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
946 - $total_pages = ceil($total_prompts / $per_page);
947 -
948 - // Retrieve prompts from the database
949 - $prompts = $wpdb->get_results(
950 - $wpdb->prepare(
951 - "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
952 - $per_page,
953 - $offset
954 - )
955 - );
956 -
957 - // Add the pagination links generation here
958 - $page_links = paginate_links(array(
959 - 'base' => add_query_arg(array(
960 - 'paged' => '%#%',
961 - 'search' => urlencode($search_query),
962 - '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
963 - ), admin_url('admin.php?page=mxchat-prompts')),
964 - 'format' => '',
965 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
966 - 'next_text' => __('Next &raquo;', 'mxchat'),
967 - 'total' => $total_pages,
968 - 'current' => $current_page,
969 - ));
970 -
971 - // Retrieve processing statuses
972 - $pdf_url = get_transient('mxchat_last_pdf_url');
973 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
974 - $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
975 - $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
976 -
977 - if ($pdf_status && $pdf_status['status'] === 'complete') {
978 - delete_transient('mxchat_last_pdf_url');
979 - $pdf_status = false;
980 - }
981 - if ($sitemap_status && $sitemap_status['status'] === 'complete') {
982 - delete_transient('mxchat_last_sitemap_url');
983 - $sitemap_status = false;
984 - }
985 -
986 - $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
987 - || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
988 - ?>
989 - <div class="wrap mxchat-wrapper">
990 - <!-- Hero Section -->
991 - <div class="mxchat-hero">
992 - <h1 class="mxchat-main-title">
993 - <span class="mxchat-gradient-text">Knowledge Base</span> Manager
994 - </h1>
995 - <p class="mxchat-hero-subtitle">
996 - <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
997 - </p>
998 - </div>
999 -
1000 - <div class="mxchat-content">
1001 - <!-- Import Settings Card -->
1002 - <div class="mxchat-card">
1003 - <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
1004 - <p><?php esc_html_e('Using the free Pinecone DB Manager add-on is recommended for high performance and a knowledge database of more than 500.', 'mxchat'); ?></p>
1005 -
1006 - <!-- Tab Contents -->
1007 - <div class="mxchat-tab-contents">
1008 - <!-- Default Database Tab -->
1009 - <div id="default-db" class="mxchat-tab-content active">
1010 - <!-- Auto-Sync Settings -->
1011 - <div class="mxchat-settings-section">
1012 - <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
1013 - <p class="mxchat-description">
1014 - <?php esc_html_e('Note: Auto-sync works only for newly published content. Existing posts and pages must be imported manually below. Works with Pinecone if enabled', 'mxchat'); ?>
1015 - </p>
1016 - <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
1017 - <div class="mxchat-toggle-group">
1018 - <div class="mxchat-toggle-container">
1019 - <label class="mxchat-toggle-switch">
1020 - <input type="checkbox"
1021 - name="mxchat_auto_sync_posts"
1022 - class="mxchat-autosave-field"
1023 - value="1"
1024 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1025 - <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
1026 - <span class="mxchat-toggle-slider"></span>
1027 - </label>
1028 - <span class="mxchat-toggle-label">
1029 - <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
1030 - </span>
1031 - </div>
1032 - <div class="mxchat-toggle-container">
1033 - <label class="mxchat-toggle-switch">
1034 - <input type="checkbox"
1035 - name="mxchat_auto_sync_pages"
1036 - class="mxchat-autosave-field"
1037 - value="1"
1038 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1039 - <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
1040 - <span class="mxchat-toggle-slider"></span>
1041 - </label>
1042 - <span class="mxchat-toggle-label">
1043 - <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
1044 - </span>
1045 - </div>
1046 - </div>
1047 - </div>
1048 - </div>
1049 -
1050 - <!-- Import Methods -->
1051 - <div class="mxchat-import-methods">
1052 - <div class="mxchat-method-card">
1053 - <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1054 - <p><span class="red-warning">IMPORTANT:</span> <?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p> </div>
1055 - <div class="mxchat-method-card">
1056 - <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1057 - <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
1058 - </div>
1059 - <div class="mxchat-method-card">
1060 - <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
1061 - <p><?php esc_html_e('Import content from any specific webpage.', 'mxchat'); ?></p>
1062 - </div>
1063 - <div class="mxchat-method-card">
1064 - <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
1065 - <p><?php esc_html_e('Directly submit content to be vectorized.', 'mxchat'); ?></p>
1066 - </div>
1067 - </div>
1068 -
1069 - <!-- Import Form -->
1070 - <?php if (!$is_processing) : ?>
1071 - <div class="mxchat-import-form">
1072 - <form id="mxchat-sitemap-form" method="post"
1073 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
1074 - <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
1075 - <div class="mxchat-url-input-group">
1076 - <input type="url"
1077 - name="sitemap_url"
1078 - id="sitemap_url"
1079 - placeholder="<?php esc_attr_e('Enter Sitemap, PDF, or Webpage URL', 'mxchat'); ?>"
1080 - required />
1081 - <button type="submit"
1082 - name="submit_sitemap"
1083 - class="mxchat-button-primary">
1084 - <?php esc_html_e('Import', 'mxchat'); ?>
1085 - </button>
1086 - </div>
1087 - </form>
1088 - </div>
1089 - <?php endif; ?>
1090 -
1091 - <!-- Processing Status -->
1092 - <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
1093 - <div class="mxchat-status-card">
1094 - <div class="mxchat-status-header">
1095 - <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1096 - <?php if ($is_processing) : ?>
1097 - <form method="post" class="mxchat-stop-form"
1098 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1099 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1100 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1101 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1102 - </button>
1103 - </form>
1104 - <?php endif; ?>
1105 - </div>
1106 - <div class="mxchat-progress-bar">
1107 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1108 - </div>
1109 - <div class="mxchat-status-details">
1110 - <p><?php printf(
1111 - esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1112 - absint($pdf_status['processed_pages']),
1113 - absint($pdf_status['total_pages']),
1114 - absint($pdf_status['percentage'])
1115 - ); ?></p>
1116 - <p><?php printf(
1117 - esc_html__('Status: %s', 'mxchat'),
1118 - esc_html(ucfirst($pdf_status['status']))
1119 - ); ?></p>
1120 - <p><?php printf(
1121 - esc_html__('Last update: %s', 'mxchat'),
1122 - esc_html($pdf_status['last_update'])
1123 - ); ?></p>
1124 - </div>
1125 - </div>
1126 - <?php endif; ?>
1127 -
1128 - <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
1129 - <div class="mxchat-status-card">
1130 - <div class="mxchat-status-header">
1131 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
1132 - <?php if ($is_processing) : ?>
1133 - <form method="post" class="mxchat-stop-form"
1134 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1135 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1136 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1137 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1138 - </button>
1139 - </form>
1140 - <?php endif; ?>
1141 - </div>
1142 - <div class="mxchat-progress-bar">
1143 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
1144 - </div>
1145 - <div class="mxchat-status-details">
1146 - <p><?php printf(
1147 - esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
1148 - absint($sitemap_status['processed_urls']),
1149 - absint($sitemap_status['total_urls']),
1150 - absint($sitemap_status['percentage'])
1151 - ); ?></p>
1152 - <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
1153 - <div class="mxchat-error-notice">
1154 - <?php if (!empty($sitemap_status['error'])) : ?>
1155 - <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
1156 - <?php endif; ?>
1157 - <?php if (!empty($sitemap_status['last_error'])) : ?>
1158 - <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
1159 - <?php endif; ?>
1160 - </div>
1161 - <?php endif; ?>
1162 - </div>
1163 - </div>
1164 - <?php endif; ?>
1165 - </div>
1166 -
1167 -
1168 - </div>
1169 - </div>
1170 -
1171 - <!-- Direct Content Submission Card -->
1172 -<div class="mxchat-card">
1173 - <div class="mxchat-card-header">
1174 - <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
1175 - </div>
1176 - <form id="mxchat-content-form" method="post"
1177 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
1178 - <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
1179 - <div class="mxchat-form-group">
1180 - <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
1181 - <textarea name="article_content" id="article_content" required
1182 - placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
1183 - </div>
1184 - <div class="mxchat-form-group">
1185 - <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
1186 - <input type="url" name="article_url" id="article_url"
1187 - placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
1188 - </div>
1189 - <button type="submit" name="submit_content" class="mxchat-button-primary">
1190 - <?php esc_html_e('Submit Content', 'mxchat'); ?>
1191 - </button>
1192 - </form>
1193 -</div>
1194 -
1195 -<!-- Knowledge Base Table Card -->
1196 -<div class="mxchat-card">
1197 - <div class="mxchat-card-header">
1198 - <h2><?php esc_html_e('Knowledge Base', 'mxchat'); ?></h2>
1199 - <div class="mxchat-header-actions">
1200 - <!-- Search -->
1201 - <form method="get" id="knowledge-search" class="mxchat-search-form">
1202 - <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
1203 - <input type="hidden" name="page" value="mxchat-prompts" />
1204 - <div class="mxchat-search-group">
1205 - <span class="dashicons dashicons-search"></span>
1206 - <input type="text"
1207 - name="search"
1208 - placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
1209 - value="<?php echo esc_attr($search_query); ?>" />
1210 - </div>
1211 - </form>
1212 -
1213 - <!-- Delete All -->
1214 - <form method="post"
1215 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
1216 - class="mxchat-delete-form"
1217 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
1218 - <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
1219 - <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
1220 - <span class="dashicons dashicons-trash"></span>
1221 - <?php esc_html_e('Delete All', 'mxchat'); ?>
1222 - </button>
1223 - </form>
1224 - </div>
1225 - </div>
1226 -
1227 - <!-- Table -->
1228 - <div class="mxchat-table-wrapper">
1229 - <table class="mxchat-records-table">
1230 - <thead>
1231 - <tr>
1232 - <th><?php esc_html_e('ID', 'mxchat'); ?></th>
1233 - <th><?php esc_html_e('Content', 'mxchat'); ?></th>
1234 - <th><?php esc_html_e('Source', 'mxchat'); ?></th>
1235 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
1236 - </tr>
1237 - </thead>
1238 - <tbody>
1239 - <?php if ($prompts) : ?>
1240 - <?php foreach ($prompts as $prompt) : ?>
1241 - <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
1242 - <td><?php echo esc_html($prompt->id); ?></td>
1243 - <td class="mxchat-content-cell">
1244 - <div class="content-view">
1245 - <?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?>
1246 - </div>
1247 - <textarea class="content-edit" style="display:none;">
1248 - <?php echo esc_textarea($prompt->article_content); ?>
1249 - </textarea>
1250 - </td>
1251 - <td class="mxchat-url-cell">
1252 - <?php if (!empty($prompt->source_url)) : ?>
1253 - <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
1254 - <span class="dashicons dashicons-external"></span>
1255 - <?php esc_html_e('View Source', 'mxchat'); ?>
1256 - </a>
1257 - <?php else : ?>
1258 - <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
1259 - <?php endif; ?>
1260 - </td>
1261 - <td class="mxchat-actions-cell">
1262 - <button class="mxchat-button-icon edit-button"
1263 - data-id="<?php echo esc_attr($prompt->id); ?>">
1264 - <span class="dashicons dashicons-edit"></span>
1265 - </button>
1266 - <button class="mxchat-button-icon save-button"
1267 - data-id="<?php echo esc_attr($prompt->id); ?>"
1268 - style="display:none;"
1269 - data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
1270 - <span class="dashicons dashicons-saved"></span>
1271 - </button>
1272 - <a href="<?php echo esc_url(admin_url(
1273 - 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1274 - . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1275 - )); ?>"
1276 - class="mxchat-button-icon delete-button"
1277 - onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1278 - <span class="dashicons dashicons-trash"></span>
1279 - </a>
1280 - </td>
1281 - </tr>
1282 - <?php endforeach; ?>
1283 - <?php else : ?>
1284 - <tr>
1285 - <td colspan="4" class="mxchat-no-records">
1286 - <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1287 - </td>
1288 - </tr>
1289 - <?php endif; ?>
1290 - </tbody>
1291 - </table>
1292 - </div>
1293 -
1294 - <?php if ($page_links) : ?>
1295 - <div class="mxchat-pagination">
1296 - <?php echo wp_kses_post($page_links); ?>
1297 - </div>
1298 - <?php endif; ?>
1299 -</div>
1300 -</div>
1301 -</div>
1302 -<?php
1303 -}
1304 -
1305 -
1306 -public function mxchat_handle_delete_all_prompts() {
1307 - // Verify nonce
1308 - if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
1309 - wp_die(__('Nonce verification failed.', 'mxchat'));
1310 - }
1311 -
1312 - // Check permissions
1313 - if (!current_user_can('manage_options')) {
1314 - wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
1315 - }
1316 -
1317 - global $wpdb;
1318 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1319 -
1320 - // Delete all prompts from the table
1321 - $wpdb->query("DELETE FROM {$table_name}");
1322 -
1323 - // Clear relevant cache
1324 - wp_cache_delete('all_prompts', 'mxchat_prompts');
1325 -
1326 - // Redirect back with a success message
1327 - $redirect_url = add_query_arg(array(
1328 - 'page' => 'mxchat-prompts',
1329 - 'all_deleted' => 'true'
1330 - ), admin_url('admin.php'));
1331 -
1332 - wp_safe_redirect($redirect_url);
1333 - exit;
1334 -}
1335 -public function mxchat_handle_delete_prompt() {
1336 - // Sanitize and validate nonce
1337 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
1338 - if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
1339 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1340 - }
1341 -
1342 - // Check permissions
1343 - if (!current_user_can('manage_options')) {
1344 - wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
1345 - }
1346 -
1347 - // Validate and sanitize ID parameter
1348 - $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
1349 - if ($id <= 0) {
1350 - wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
1351 - }
1352 -
1353 - global $wpdb;
1354 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1355 -
1356 - // Clear cache and delete prompt
1357 - wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
1358 - $wpdb->delete($table_name, array('id' => $id), array('%d'));
1359 -
1360 - wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1361 - exit;
1362 -}
1363 -
1364 -
1365 -public function mxchat_generate_embedding($text) {
1366 - //error_log('Starting embedding generation for text: ' . substr($text, 0, 100) . '...');
1367 -
1368 - $options = get_option('mxchat_options');
1369 - $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
1370 - //error_log('Selected embedding model: ' . $selected_model);
1371 -
1372 - // Determine provider and endpoint
1373 - if (strpos($selected_model, 'voyage') === 0) {
1374 - $api_key = $options['voyage_api_key'] ?? '';
1375 - $endpoint = 'https://api.voyageai.com/v1/embeddings';
1376 - } else {
1377 - $api_key = $options['api_key'] ?? '';
1378 - $endpoint = 'https://api.openai.com/v1/embeddings';
1379 - }
1380 - //error_log('Using endpoint: ' . $endpoint);
1381 -
1382 - if (empty($api_key)) {
1383 - //error_log('Error: API key not configured');
1384 - return null;
1385 - }
1386 -
1387 - // Prepare request body
1388 - $request_body = array(
1389 - 'model' => $selected_model,
1390 - 'input' => $text
1391 - );
1392 -
1393 - // Add output_dimension for voyage-3-large model
1394 - if ($selected_model === 'voyage-3-large') {
1395 - $request_body['output_dimension'] = 2048;
1396 - }
1397 -
1398 - //error_log('Request body prepared: ' . wp_json_encode($request_body));
1399 -
1400 - // Make API request
1401 - $response = wp_remote_post($endpoint, array(
1402 - 'body' => wp_json_encode($request_body),
1403 - 'headers' => array(
1404 - 'Authorization' => 'Bearer ' . $api_key,
1405 - 'Content-Type' => 'application/json'
1406 - ),
1407 - 'timeout' => 30
1408 - ));
1409 -
1410 - // Handle wp_remote_post errors
1411 - if (is_wp_error($response)) {
1412 - //error_log('WP Remote Post Error: ' . $response->get_error_message());
1413 - return null;
1414 - }
1415 -
1416 - // Get and check HTTP response code
1417 - $http_code = wp_remote_retrieve_response_code($response);
1418 - //error_log('API Response Code: ' . $http_code);
1419 -
1420 - if ($http_code !== 200) {
1421 - //error_log('API Error Response Body: ' . wp_remote_retrieve_body($response));
1422 - return null;
1423 - }
1424 -
1425 - // Parse response body
1426 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1427 -
1428 - if (json_last_error() !== JSON_ERROR_NONE) {
1429 - //error_log('JSON Parse Error: ' . json_last_error_msg());
1430 - return null;
1431 - }
1432 -
1433 - // Both APIs use the same structure, so we can extract the embedding the same way
1434 - if (isset($response_data['data'][0]['embedding'])) {
1435 - //error_log('Successfully extracted embedding');
1436 - //error_log('Embedding dimension: ' . count($response_data['data'][0]['embedding']));
1437 - return $response_data['data'][0]['embedding'];
1438 - } else {
1439 - //error_log('Error: No embedding found in response. Response structure: ' . wp_json_encode($response_data));
1440 - return null;
1441 - }
1442 -}
1443 -
1444 -
1445 -public function mxchat_delete_chat_history() {
1446 - if (!current_user_can('manage_options')) {
1447 - echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1448 - wp_die();
1449 - }
1450 -
1451 - check_ajax_referer('mxchat_delete_chat_history', 'security');
1452 -
1453 - global $wpdb;
1454 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1455 -
1456 - if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
1457 - foreach ($_POST['delete_session_ids'] as $session_id) {
1458 - $session_id_sanitized = sanitize_text_field($session_id);
1459 -
1460 - // Clear relevant cache before deletion
1461 - $cache_key = 'chat_session_' . $session_id_sanitized;
1462 - wp_cache_delete($cache_key, 'mxchat_chat_sessions');
1463 -
1464 - // Perform the deletion
1465 - $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
1466 - }
1467 -
1468 - // Optionally, clear a general cache if you have one
1469 - wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
1470 -
1471 - echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]);
1472 - } else {
1473 - echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
1474 - }
1475 -
1476 - wp_die();
1477 - }
1478 -
1479 -
1480 -public function mxchat_save_inline_prompt() {
1481 - // Check for nonce security
1482 - check_ajax_referer('mxchat_save_inline_nonce');
1483 -
1484 - // Verify permissions
1485 - if (!current_user_can('manage_options')) {
1486 - wp_send_json_error(esc_html__('Permission denied.', 'mxchat'));
1487 - return;
1488 - }
1489 -
1490 - global $wpdb;
1491 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1492 -
1493 - // Validate and sanitize input data
1494 - $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1495 - $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1496 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1497 -
1498 - if ($prompt_id > 0 && !empty($article_content)) {
1499 - // Re-generate the embedding vector for the updated content
1500 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1501 -
1502 - if (is_array($embedding_vector)) {
1503 - // Serialize the embedding vector before storing it
1504 - $embedding_vector_serialized = serialize($embedding_vector);
1505 -
1506 - // Update the prompt in the database
1507 - $updated = $wpdb->update(
1508 - $table_name,
1509 - array(
1510 - 'article_content' => $article_content,
1511 - 'embedding_vector' => $embedding_vector_serialized,
1512 - 'source_url' => $article_url,
1513 - ),
1514 - array('id' => $prompt_id),
1515 - array('%s', '%s', '%s'),
1516 - array('%d')
1517 - );
1518 -
1519 - if ($updated !== false) {
1520 - wp_send_json_success();
1521 - } else {
1522 - wp_send_json_error(esc_html__('Database update failed.', 'mxchat'));
1523 - }
1524 - } else {
1525 - wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat'));
1526 - }
1527 - } else {
1528 - wp_send_json_error(esc_html__('Invalid data.', 'mxchat'));
1529 - }
1530 -}
1531 -
1532 -
1533 -
1534 -// Add this method to handle post updates
1535 -public function handle_post_update($post_id, $post, $update) {
1536 - // Basic validation checks
1537 - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1538 - return;
1539 - }
1540 -
1541 - // Only process published content
1542 - if (!in_array($post->post_status, array('publish'))) {
1543 - return;
1544 - }
1545 -
1546 - // Check if sync is enabled for this post type
1547 - $post_type = $post->post_type;
1548 - if (!in_array($post_type, array('post', 'page'))) {
1549 - return;
1550 - }
1551 -
1552 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1553 - if (get_option($sync_option) != '1') {
1554 - return;
1555 - }
1556 -
1557 - // Prepare content and URL
1558 - $content = wp_strip_all_tags($post->post_content);
1559 - $url = get_permalink($post_id);
1560 -
1561 - // Generate embedding vector
1562 - $embedding_vector = $this->mxchat_generate_embedding($content);
1563 - if (!$embedding_vector) {
1564 - return;
1565 - }
1566 -
1567 - // Check for Pinecone addon and its settings
1568 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1569 - $use_pinecone = false;
1570 -
1571 - if ($pinecone_settings && is_array($pinecone_settings)) {
1572 - // Check if Pinecone is enabled and all required settings are present
1573 - $use_pinecone = (
1574 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1575 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1576 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1577 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1578 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1579 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1580 - );
1581 - }
1582 -
1583 - if ($use_pinecone) {
1584 - // Use Pinecone
1585 - $pinecone_result = $this->store_in_pinecone_main(
1586 - $embedding_vector,
1587 - $content,
1588 - $url,
1589 - $pinecone_settings['mxchat_pinecone_api_key'],
1590 - $pinecone_settings['mxchat_pinecone_environment'],
1591 - $pinecone_settings['mxchat_pinecone_index']
1592 - );
1593 -
1594 - if (!$pinecone_result['success']) {
1595 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1596 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1597 - }
1598 - } else {
1599 - // Fallback to WordPress DB storage
1600 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1601 - }
1602 -}
1603 -// Modified storage function to add type field
1604 -private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1605 - $vector_id = $vector_id ?: md5($url);
1606 - $options = get_option('mxchat_pinecone_addon_options');
1607 - $host = $options['mxchat_pinecone_host'] ?? '';
1608 -
1609 - if (empty($host)) {
1610 - return array(
1611 - 'success' => false,
1612 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1613 - );
1614 - }
1615 -
1616 - // Determine if this is a product URL
1617 - $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1618 -
1619 - $api_endpoint = "https://{$host}/vectors/upsert";
1620 - $request_body = array(
1621 - 'vectors' => array(
1622 - array(
1623 - 'id' => $vector_id,
1624 - 'values' => $embedding_vector,
1625 - 'metadata' => array(
1626 - 'text' => $content,
1627 - 'source_url' => $url,
1628 - 'type' => $is_product ? 'product' : 'content',
1629 - 'last_updated' => time()
1630 - )
1631 - )
1632 - )
1633 - );
1634 -
1635 - $response = wp_remote_post($api_endpoint, array(
1636 - 'headers' => array(
1637 - 'Api-Key' => $api_key,
1638 - 'accept' => 'application/json',
1639 - 'content-type' => 'application/json'
1640 - ),
1641 - 'body' => wp_json_encode($request_body),
1642 - 'timeout' => 30,
1643 - 'data_format' => 'body'
1644 - ));
1645 -
1646 - if (is_wp_error($response)) {
1647 - return array(
1648 - 'success' => false,
1649 - 'message' => $response->get_error_message()
1650 - );
1651 - }
1652 -
1653 - $response_code = wp_remote_retrieve_response_code($response);
1654 - if ($response_code !== 200) {
1655 - $body = wp_remote_retrieve_body($response);
1656 - return array(
1657 - 'success' => false,
1658 - 'message' => sprintf(
1659 - 'Pinecone API error (HTTP %d): %s',
1660 - $response_code,
1661 - $body
1662 - )
1663 - );
1664 - }
1665 -
1666 - return array(
1667 - 'success' => true,
1668 - 'message' => 'Successfully stored in Pinecone'
1669 - );
1670 -}
1671 -// Helper method for WordPress DB storage
1672 -private function store_in_wordpress_db($content, $url, $embedding_vector) {
1673 - global $wpdb;
1674 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1675 -
1676 - $existing_entry = $wpdb->get_row(
1677 - $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url)
1678 - );
1679 -
1680 - if ($existing_entry) {
1681 - $wpdb->update(
1682 - $table_name,
1683 - array(
1684 - 'article_content' => $content,
1685 - 'embedding_vector' => serialize($embedding_vector),
1686 - 'timestamp' => current_time('mysql')
1687 - ),
1688 - array('id' => $existing_entry->id),
1689 - array('%s', '%s', '%s')
1690 - );
1691 - } else {
1692 - $wpdb->insert(
1693 - $table_name,
1694 - array(
1695 - 'article_content' => $content,
1696 - 'source_url' => $url,
1697 - 'embedding_vector' => serialize($embedding_vector),
1698 - 'timestamp' => current_time('mysql')
1699 - ),
1700 - array('%s', '%s', '%s', '%s')
1701 - );
1702 - }
1703 -}
1704 -/**
1705 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1706 - *
1707 - * @param int $post_id The ID of the post being deleted
1708 - * @return void
1709 - */
1710 -public function mxchat_handle_post_delete($post_id) {
1711 - // Get post data before it's deleted
1712 - $post = get_post($post_id);
1713 -
1714 - // Basic validation
1715 - if (!$post || wp_is_post_revision($post_id)) {
1716 - return;
1717 - }
1718 -
1719 - // Check post type
1720 - $post_type = $post->post_type;
1721 - if (!in_array($post_type, array('post', 'page'))) {
1722 - return;
1723 - }
1724 -
1725 - // Check if sync is enabled for this post type
1726 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1727 - if (get_option($sync_option) != '1') {
1728 - return;
1729 - }
1730 -
1731 - // Get the URL before post is deleted
1732 - $source_url = get_permalink($post_id);
1733 - if (!$source_url) {
1734 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1735 - return;
1736 - }
1737 -
1738 - // Check for Pinecone addon and its settings
1739 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1740 - $use_pinecone = false;
1741 - if ($pinecone_settings && is_array($pinecone_settings)) {
1742 - $use_pinecone = (
1743 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1744 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1745 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1746 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1747 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1748 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1749 - );
1750 - }
1751 -
1752 - $deletion_successful = false;
1753 -
1754 - if ($use_pinecone) {
1755 - try {
1756 - $pinecone_result = $this->delete_from_pinecone(
1757 - array($source_url),
1758 - $pinecone_settings['mxchat_pinecone_api_key'],
1759 - $pinecone_settings['mxchat_pinecone_environment'],
1760 - $pinecone_settings['mxchat_pinecone_index']
1761 - );
1762 -
1763 - if (!$pinecone_result['success']) {
1764 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1765 - } else {
1766 - $deletion_successful = true;
1767 - }
1768 - } catch (Exception $e) {
1769 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1770 - }
1771 - }
1772 -
1773 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1774 - try {
1775 - global $wpdb;
1776 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1777 -
1778 - $result = $wpdb->delete(
1779 - $table_name,
1780 - array('source_url' => $source_url),
1781 - array('%s')
1782 - );
1783 -
1784 - if ($result === false) {
1785 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1786 - } else {
1787 - $deletion_successful = true;
1788 - }
1789 - } catch (Exception $e) {
1790 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1791 - }
1792 -
1793 - if (!$deletion_successful) {
1794 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1795 - }
1796 -}
1797 -
1798 -
1799 -public function mxchat_handle_content_submission() {
1800 - // Check if the form was submitted and the user has permission.
1801 - if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1802 - return;
1803 - }
1804 -
1805 - // Verify the nonce.
1806 - $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1807 - if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1808 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1809 - }
1810 -
1811 - // Sanitize the inputs.
1812 - $article_content = sanitize_textarea_field($_POST['article_content']);
1813 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1814 -
1815 - // Generate the embedding vector.
1816 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1817 - if (!is_array($embedding_vector)) {
1818 - set_transient('mxchat_admin_notice_error',
1819 - esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1820 - 30
1821 - );
1822 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1823 - exit;
1824 - }
1825 -
1826 - // Store in the WordPress database.
1827 - global $wpdb;
1828 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1829 -
1830 - // Ensure the source_url column exists.
1831 - if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1832 - $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1833 - }
1834 -
1835 - // Serialize and store the embedding vector.
1836 - $embedding_vector_serialized = serialize($embedding_vector);
1837 - $inserted = $wpdb->insert(
1838 - $table_name,
1839 - array(
1840 - 'article_content' => $article_content,
1841 - 'embedding_vector' => $embedding_vector_serialized,
1842 - 'source_url' => $article_url,
1843 - ),
1844 - array('%s', '%s', '%s')
1845 - );
1846 -
1847 - if ($inserted === false) {
1848 - set_transient('mxchat_admin_notice_error',
1849 - esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1850 - 30
1851 - );
1852 - } else {
1853 - set_transient('mxchat_admin_notice_success',
1854 - esc_html__('Content successfully submitted!', 'mxchat'),
1855 - 30
1856 - );
1857 - }
1858 -
1859 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1860 - exit;
1861 -}
1862 -
1863 -private function is_pdf_url($url, $response) {
1864 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1865 - $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1866 -
1867 - return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1868 -}
1869 -private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1870 - if (!current_user_can('manage_options')) {
1871 - //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat'));
1872 - return false;
1873 - }
1874 -
1875 - $pdf_url = esc_url_raw($pdf_url);
1876 - $upload_dir = wp_upload_dir();
1877 -
1878 - if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1879 - //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error'])));
1880 - return false;
1881 - }
1882 -
1883 - $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1884 - $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1885 -
1886 - $response_body = wp_remote_retrieve_body($response);
1887 - if (empty($response_body)) {
1888 - //error_log(esc_html__('Empty PDF response body', 'mxchat'));
1889 - return false;
1890 - }
1891 -
1892 - if (!wp_mkdir_p(dirname($pdf_path))) {
1893 - //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path)));
1894 - return false;
1895 - }
1896 -
1897 - try {
1898 - file_put_contents($pdf_path, $response_body);
1899 -
1900 - if (!file_exists($pdf_path)) {
1901 - throw new Exception(__('Failed to save PDF file', 'mxchat'));
1902 - }
1903 -
1904 - $parser = new \Smalot\PdfParser\Parser();
1905 - $pdf = $parser->parseFile($pdf_path);
1906 - $total_pages = absint(count($pdf->getPages()));
1907 -
1908 - if ($total_pages < 1) {
1909 - throw new Exception(__('Invalid PDF: no pages found', 'mxchat'));
1910 - }
1911 -
1912 - wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1913 - 'pdf_path' => $pdf_path,
1914 - 'pdf_url' => $pdf_url,
1915 - 'total_pages' => $total_pages,
1916 - 'batch_size' => absint(15),
1917 - 'batch_pause' => absint(10)
1918 - ));
1919 -
1920 - $status_data = array(
1921 - 'total_pages' => $total_pages,
1922 - 'processed_pages' => 0,
1923 - 'status' => 'processing',
1924 - 'last_update' => time()
1925 - );
1926 -
1927 - set_transient(
1928 - sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1929 - array_map('sanitize_text_field', $status_data),
1930 - DAY_IN_SECONDS
1931 - );
1932 -
1933 - return __('scheduled', 'mxchat');
1934 -
1935 - } catch (Exception $e) {
1936 - //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1937 - if (file_exists($pdf_path)) {
1938 - wp_delete_file($pdf_path);
1939 - }
1940 - return false;
1941 - }
1942 -}
1943 -public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1944 - // Validate inputs
1945 - $pdf_path = sanitize_text_field($pdf_path);
1946 - $pdf_url = esc_url_raw($pdf_url);
1947 - $total_pages = absint($total_pages);
1948 - $batch_size = absint($batch_size);
1949 - $batch_pause = absint($batch_pause);
1950 -
1951 - try {
1952 - if (!file_exists($pdf_path)) {
1953 - throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1954 - }
1955 -
1956 - $parser = new \Smalot\PdfParser\Parser();
1957 - $pdf = $parser->parseFile($pdf_path);
1958 - $pages = $pdf->getPages();
1959 -
1960 - // Get current progress
1961 - $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1962 - $status = get_transient($status_key);
1963 -
1964 - if (!$status || !is_array($status)) {
1965 - throw new Exception('Invalid status data retrieved from transient');
1966 - }
1967 -
1968 - $start_page = absint($status['processed_pages']);
1969 - $end_page = min($start_page + $batch_size, $total_pages);
1970 -
1971 - $instance = new self(); // Create an instance of the class
1972 -
1973 - for ($i = $start_page; $i < $end_page; $i++) {
1974 - $text = $pages[$i]->getText();
1975 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1976 -
1977 - if (!empty($sanitized_content)) {
1978 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1979 - if (is_array($embedding_vector)) {
1980 - $metadata = array(
1981 - 'document_type' => 'pdf',
1982 - 'total_pages' => $total_pages,
1983 - 'current_page' => $i + 1,
1984 - 'prev_page' => $i > 0 ? $i : null,
1985 - 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1986 - 'source_url' => $pdf_url
1987 - );
1988 -
1989 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1990 - $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1991 -
1992 - $options = get_option('mxchat_options');
1993 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1994 - }
1995 - }
1996 -
1997 - // Update progress with sanitized data
1998 - $status['processed_pages'] = absint($i + 1);
1999 - $status['last_update'] = time();
2000 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2001 - }
2002 -
2003 - // Schedule next batch if needed
2004 - if ($end_page < $total_pages) {
2005 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
2006 - 'pdf_path' => $pdf_path,
2007 - 'pdf_url' => $pdf_url,
2008 - 'total_pages' => $total_pages,
2009 - 'batch_size' => $batch_size,
2010 - 'batch_pause' => $batch_pause
2011 - ));
2012 - } else {
2013 - // Processing complete
2014 - $status['status'] = 'complete';
2015 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2016 - if (file_exists($pdf_path)) {
2017 - wp_delete_file($pdf_path);
2018 - }
2019 - }
2020 -
2021 - } catch (\Exception $e) {
2022 - $status['status'] = 'error';
2023 - $status['error'] = sanitize_text_field($e->getMessage());
2024 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2025 - if (file_exists($pdf_path)) {
2026 - wp_delete_file($pdf_path);
2027 - }
2028 - }
2029 -}
2030 -public function get_pdf_processing_status($pdf_url) {
2031 - $pdf_url = esc_url_raw($pdf_url);
2032 - $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
2033 -
2034 - if (!$status || !is_array($status)) {
2035 - return false;
2036 - }
2037 -
2038 - return array(
2039 - 'total_pages' => absint($status['total_pages']),
2040 - 'processed_pages' => absint($status['processed_pages']),
2041 - 'percentage' => ($status['total_pages'] > 0)
2042 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
2043 - : 0,
2044 - 'status' => sanitize_text_field($status['status']),
2045 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
2046 - );
2047 -}
2048 -public function mxchat_handle_sitemap_submission() {
2049 - // Check if the form was submitted and verify permissions
2050 - if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
2051 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
2052 - }
2053 -
2054 - // Verify nonce
2055 - check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
2056 -
2057 - // Validate URL
2058 - if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
2059 - set_transient('mxchat_admin_notice_error',
2060 - esc_html__('Please provide a valid URL.', 'mxchat'),
2061 - 30
2062 - );
2063 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2064 - exit;
2065 - }
2066 -
2067 - $submitted_url = esc_url_raw($_POST['sitemap_url']);
2068 - $response = wp_remote_get($submitted_url, array('timeout' => 30));
2069 -
2070 - if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
2071 - $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat');
2072 - set_transient('mxchat_admin_notice_error',
2073 - sprintf(
2074 - esc_html__('Failed to fetch the URL: %s', 'mxchat'),
2075 - esc_html($error_message)
2076 - ),
2077 - 30
2078 - );
2079 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2080 - exit;
2081 - }
2082 -
2083 - $content_type = wp_remote_retrieve_header($response, 'content-type');
2084 - $body_content = wp_remote_retrieve_body($response);
2085 -
2086 - if (empty($body_content)) {
2087 - set_transient('mxchat_admin_notice_error',
2088 - esc_html__('Empty response received from URL.', 'mxchat'),
2089 - 30
2090 - );
2091 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2092 - exit;
2093 - }
2094 -
2095 - // Handle PDF URL
2096 - if ($this->is_pdf_url($submitted_url, $response)) {
2097 - $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
2098 -
2099 - if ($result === 'scheduled') {
2100 - set_transient(
2101 - 'mxchat_last_pdf_url',
2102 - sanitize_text_field($submitted_url),
2103 - DAY_IN_SECONDS
2104 - );
2105 - set_transient('mxchat_admin_notice_info',
2106 - esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
2107 - 30
2108 - );
2109 - } else {
2110 - set_transient('mxchat_admin_notice_error',
2111 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
2112 - 30
2113 - );
2114 - }
2115 -
2116 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2117 - exit;
2118 - }
2119 -
2120 - // Handle Sitemap XML
2121 - if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
2122 - libxml_use_internal_errors(true);
2123 - $xml = simplexml_load_string($body_content);
2124 - $xml_errors = libxml_get_errors();
2125 - libxml_clear_errors();
2126 -
2127 - if ($xml === false || !empty($xml_errors)) {
2128 - set_transient('mxchat_admin_notice_error',
2129 - esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
2130 - 30
2131 - );
2132 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2133 - exit;
2134 - }
2135 -
2136 - $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
2137 -
2138 - if ($result === 'scheduled') {
2139 - set_transient(
2140 - 'mxchat_last_sitemap_url',
2141 - sanitize_text_field($submitted_url),
2142 - DAY_IN_SECONDS
2143 - );
2144 - set_transient('mxchat_admin_notice_info',
2145 - esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
2146 - 30
2147 - );
2148 - } else {
2149 - set_transient('mxchat_admin_notice_error',
2150 - esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
2151 - 30
2152 - );
2153 - }
2154 -
2155 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2156 - exit;
2157 - }
2158 -
2159 - // Handle Regular URL
2160 - $page_content = $this->mxchat_extract_main_content($body_content);
2161 - $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
2162 -
2163 - if (empty($sanitized_content)) {
2164 - set_transient('mxchat_admin_notice_error',
2165 - esc_html__('No valid content found on the provided URL.', 'mxchat'),
2166 - 30
2167 - );
2168 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2169 - exit;
2170 - }
2171 -
2172 - $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
2173 - if (is_array($embedding_vector)) {
2174 - MxChat_Utils::submit_content_to_db(
2175 - $sanitized_content,
2176 - $submitted_url,
2177 - $this->options['api_key']
2178 - );
2179 - set_transient('mxchat_admin_notice_success',
2180 - esc_html__('URL content successfully submitted!', 'mxchat'),
2181 - 30
2182 - );
2183 - } else {
2184 - set_transient('mxchat_admin_notice_error',
2185 - esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
2186 - 30
2187 - );
2188 - }
2189 -
2190 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
2191 - exit;
2192 -}
2193 -private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
2194 - if (!current_user_can('manage_options')) {
2195 - //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
2196 - return false;
2197 - }
2198 -
2199 - try {
2200 - $sitemap_url = esc_url_raw($sitemap_url);
2201 -
2202 - if (!$xml || !is_object($xml)) {
2203 - throw new Exception(__('Invalid XML object provided', 'mxchat'));
2204 - }
2205 -
2206 - $urls = [];
2207 - foreach ($xml->url as $url_element) {
2208 - $url = esc_url_raw((string)$url_element->loc);
2209 - if ($url) {
2210 - $urls[] = $url;
2211 - }
2212 - }
2213 -
2214 - $total_urls = absint(count($urls));
2215 -
2216 - if ($total_urls < 1) {
2217 - throw new Exception(__('No valid URLs found in sitemap', 'mxchat'));
2218 - }
2219 -
2220 - wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
2221 - 'urls' => $urls,
2222 - 'sitemap_url' => $sitemap_url,
2223 - 'total_urls' => $total_urls,
2224 - 'batch_size' => absint(10),
2225 - 'batch_pause' => absint(5)
2226 - ));
2227 -
2228 - $status_data = array(
2229 - 'total_urls' => $total_urls,
2230 - 'processed_urls' => 0,
2231 - 'status' => 'processing',
2232 - 'last_update' => time()
2233 - );
2234 -
2235 - set_transient(
2236 - sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
2237 - array_map('sanitize_text_field', $status_data),
2238 - DAY_IN_SECONDS
2239 - );
2240 -
2241 - return __('scheduled', 'mxchat');
2242 -
2243 - } catch (\Exception $e) {
2244 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
2245 - return false;
2246 - }
2247 -}
2248 -public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
2249 - // Validate inputs
2250 - $sitemap_url = esc_url_raw($sitemap_url);
2251 - $total_urls = absint($total_urls);
2252 - $batch_size = absint($batch_size);
2253 - $batch_pause = absint($batch_pause);
2254 -
2255 - if (!is_array($urls) || empty($urls)) {
2256 - return;
2257 - }
2258 -
2259 - try {
2260 - $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
2261 - $status = get_transient($status_key);
2262 -
2263 - if (!$status || !is_array($status)) {
2264 - throw new Exception('Invalid status data retrieved from transient');
2265 - }
2266 -
2267 - $start_url = absint($status['processed_urls']);
2268 - $end_url = min($start_url + $batch_size, $total_urls);
2269 - $instance = new self();
2270 -
2271 - // Track failures in batch
2272 - $batch_stats = [
2273 - 'processed' => 0,
2274 - 'failed' => 0,
2275 - 'last_error' => ''
2276 - ];
2277 -
2278 - for ($i = $start_url; $i < $end_url; $i++) {
2279 - $page_url = esc_url_raw($urls[$i]);
2280 - $page_response = wp_remote_get($page_url);
2281 -
2282 - if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
2283 - $batch_stats['failed']++;
2284 - continue;
2285 - }
2286 -
2287 - $page_html = wp_remote_retrieve_body($page_response);
2288 - $page_content = $instance->mxchat_extract_main_content($page_html);
2289 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2290 -
2291 - if (!empty($sanitized_content)) {
2292 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2293 -
2294 - if (is_array($embedding_vector)) {
2295 - $options = get_option('mxchat_options');
2296 - $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2297 -
2298 - if (is_wp_error($submission_result)) {
2299 - $batch_stats['failed']++;
2300 - $batch_stats['last_error'] = $submission_result->get_error_message();
2301 - continue;
2302 - }
2303 -
2304 - $batch_stats['processed']++;
2305 - } else {
2306 - $batch_stats['failed']++;
2307 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check embedding API key.';
2308 - }
2309 - }
2310 -
2311 - $status['processed_urls'] = absint($i + 1);
2312 - $status['last_update'] = time();
2313 - $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
2314 - $status['last_error'] = $batch_stats['last_error'];
2315 -
2316 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2317 - }
2318 -
2319 - // If all URLs in this batch failed, stop processing
2320 - if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
2321 - $status['status'] = 'error';
2322 - $status['error'] = sprintf(
2323 - 'Processing stopped: %d consecutive failures. Last error: %s',
2324 - $batch_stats['failed'],
2325 - $batch_stats['last_error']
2326 - );
2327 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2328 - return;
2329 - }
2330 -
2331 - if ($end_url < $total_urls) {
2332 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2333 - 'urls' => array_map('esc_url_raw', $urls),
2334 - 'sitemap_url' => $sitemap_url,
2335 - 'total_urls' => $total_urls,
2336 - 'batch_size' => $batch_size,
2337 - 'batch_pause' => $batch_pause,
2338 - ));
2339 - } else {
2340 - $status['status'] = 'complete';
2341 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2342 - }
2343 - } catch (\Exception $e) {
2344 - $status['status'] = 'error';
2345 - $status['error'] = sanitize_text_field($e->getMessage());
2346 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2347 - }
2348 -}
2349 -
2350 -public function get_sitemap_processing_status($sitemap_url) {
2351 - $sitemap_url = esc_url_raw($sitemap_url);
2352 - $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2353 -
2354 - if (!$status || !is_array($status)) {
2355 - return false;
2356 - }
2357 -
2358 - return array(
2359 - 'total_urls' => absint($status['total_urls']),
2360 - 'processed_urls' => absint($status['processed_urls']),
2361 - 'failed_urls' => absint($status['failed_urls'] ?? 0),
2362 - 'percentage' => ($status['total_urls'] > 0)
2363 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2364 - : 0,
2365 - 'status' => sanitize_text_field($status['status']),
2366 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2367 - 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2368 - 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
2369 - );
2370 -}
2371 -
2372 -public function mxchat_stop_processing() {
2373 - // Verify permissions
2374 - if (!current_user_can('manage_options')) {
2375 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
2376 - }
2377 -
2378 - // Verify nonce
2379 - check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
2380 -
2381 - // Get the last sitemap URL and clear its transient
2382 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
2383 - if ($sitemap_url) {
2384 - delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
2385 - delete_transient('mxchat_last_sitemap_url');
2386 - }
2387 -
2388 - // Get the last PDF URL and clear its transient
2389 - $pdf_url = get_transient('mxchat_last_pdf_url');
2390 - if ($pdf_url) {
2391 - delete_transient('mxchat_pdf_status_' . md5($pdf_url));
2392 - delete_transient('mxchat_last_pdf_url');
2393 - }
2394 -
2395 - // Unschedule any pending sitemap events
2396 - $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
2397 - if ($timestamp) {
2398 - wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
2399 - }
2400 -
2401 - // Redirect back with a success message
2402 - set_transient('mxchat_admin_notice_success',
2403 - esc_html__('Processing has been stopped successfully.', 'mxchat'),
2404 - 30
2405 - );
2406 - wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2407 - exit;
2408 -}
2409 -private function mxchat_sanitize_content_for_api($content) {
2410 - // Remove script, style tags, and HTML comments
2411 - $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2412 - $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2413 - $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2414 -
2415 - // Remove all HTML tags and decode HTML entities
2416 - $content = wp_strip_all_tags($content);
2417 - $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2418 -
2419 - // Trim and normalize whitespace
2420 - $content = trim(preg_replace('/\s+/', ' ', $content));
2421 -
2422 - return $content;
2423 -}
2424 -
2425 -
2426 -
2427 -
2428 -public function mxchat_handle_product_change($post_id, $post, $update) {
2429 - if ($post->post_type !== 'product') {
2430 - return;
2431 - }
2432 -
2433 - if ($post->post_status === 'publish') {
2434 - add_action('shutdown', function() use ($post_id) {
2435 - $product = wc_get_product($post_id);
2436 - if ($product) {
2437 - $this->mxchat_store_product_embedding($product);
2438 - }
2439 - });
2440 - }
2441 -}
2442 -
2443 -private function mxchat_store_product_embedding($product) {
2444 - if (!isset($this->options['enable_woocommerce_integration']) ||
2445 - !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2446 - return;
2447 - }
2448 -
2449 - $source_url = get_permalink($product->get_id());
2450 -
2451 - // Build description
2452 - $regular_price = $product->get_regular_price();
2453 - $sale_price = $product->get_sale_price();
2454 - $description = $product->get_description() . "\n\n" .
2455 - esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" .
2456 - esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" .
2457 - esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" .
2458 - esc_html__('SKU:', 'mxchat') . " " . $product->get_sku();
2459 -
2460 - // Generate embedding vector
2461 - $embedding_vector = $this->mxchat_generate_embedding($description);
2462 - if (!is_array($embedding_vector)) {
2463 - return;
2464 - }
2465 -
2466 - // Check for Pinecone addon and its settings
2467 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2468 - $use_pinecone = false;
2469 -
2470 - if ($pinecone_settings && is_array($pinecone_settings)) {
2471 - $use_pinecone = (
2472 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2473 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2474 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2475 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2476 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2477 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2478 - );
2479 - }
2480 -
2481 - if ($use_pinecone) {
2482 - $result = $this->store_in_pinecone_main(
2483 - $embedding_vector,
2484 - $description,
2485 - $source_url,
2486 - $pinecone_settings['mxchat_pinecone_api_key'],
2487 - $pinecone_settings['mxchat_pinecone_environment'],
2488 - $pinecone_settings['mxchat_pinecone_index']
2489 - );
2490 -
2491 - if (!$result['success']) {
2492 - // Fallback to WordPress DB if Pinecone storage fails
2493 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2494 - }
2495 - } else {
2496 - // Use WordPress DB storage
2497 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2498 - }
2499 -}
2500 -
2501 -public function mxchat_handle_product_delete($post_id) {
2502 - if (get_post_type($post_id) !== 'product') {
2503 - return;
2504 - }
2505 -
2506 - $source_url = get_permalink($post_id);
2507 -
2508 - // Check for Pinecone addon and its settings
2509 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2510 - $use_pinecone = false;
2511 -
2512 - if ($pinecone_settings && is_array($pinecone_settings)) {
2513 - $use_pinecone = (
2514 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2515 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2516 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2517 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2518 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2519 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2520 - );
2521 - }
2522 -
2523 - if ($use_pinecone) {
2524 - $this->delete_from_pinecone(
2525 - array($source_url),
2526 - $pinecone_settings['mxchat_pinecone_api_key'],
2527 - $pinecone_settings['mxchat_pinecone_environment'],
2528 - $pinecone_settings['mxchat_pinecone_index']
2529 - );
2530 - } else {
2531 - // Fallback to WordPress DB deletion
2532 - global $wpdb;
2533 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2534 - $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2535 - }
2536 -}
2537 -
2538 -
2539 -/**
2540 - * Delete vectors from Pinecone by source URL
2541 - *
2542 - * @param array $urls Array of source URLs to delete
2543 - * @param string $api_key Pinecone API key
2544 - * @param string $environment Pinecone environment
2545 - * @param string $index_name Pinecone index name
2546 - * @return array Associative array with 'success' boolean and 'message' string
2547 - */
2548 -private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2549 - // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2550 - $options = get_option('mxchat_pinecone_addon_options');
2551 - $host = $options['mxchat_pinecone_host'] ?? '';
2552 -
2553 - if (empty($host)) {
2554 - return array(
2555 - 'success' => false,
2556 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2557 - );
2558 - }
2559 -
2560 - // Build API endpoint using the configured host
2561 - $api_endpoint = "https://{$host}/vectors/delete";
2562 -
2563 - // Create vector IDs from URLs (matching your store method's ID generation)
2564 - $vector_ids = array_map('md5', $urls);
2565 -
2566 - // Prepare the delete request body
2567 - $request_body = array(
2568 - 'ids' => $vector_ids,
2569 - 'filter' => array(
2570 - 'source_url' => array(
2571 - '$in' => $urls
2572 - )
2573 - )
2574 - );
2575 -
2576 - // Make the deletion request
2577 - $response = wp_remote_post($api_endpoint, array(
2578 - 'headers' => array(
2579 - 'Api-Key' => $api_key,
2580 - 'accept' => 'application/json',
2581 - 'content-type' => 'application/json'
2582 - ),
2583 - 'body' => wp_json_encode($request_body),
2584 - 'timeout' => 30,
2585 - 'data_format' => 'body'
2586 - ));
2587 -
2588 - // Handle WordPress HTTP API errors
2589 - if (is_wp_error($response)) {
2590 - return array(
2591 - 'success' => false,
2592 - 'message' => $response->get_error_message()
2593 - );
2594 - }
2595 -
2596 - // Check response status
2597 - $response_code = wp_remote_retrieve_response_code($response);
2598 - if ($response_code !== 200) {
2599 - $body = wp_remote_retrieve_body($response);
2600 - return array(
2601 - 'success' => false,
2602 - 'message' => sprintf(
2603 - 'Pinecone API error (HTTP %d): %s',
2604 - $response_code,
2605 - $body
2606 - )
2607 - );
2608 - }
2609 -
2610 - // Parse response body
2611 - $body = wp_remote_retrieve_body($response);
2612 - $response_data = json_decode($body, true);
2613 -
2614 - // Final validation of the response
2615 - if (json_last_error() !== JSON_ERROR_NONE) {
2616 - return array(
2617 - 'success' => false,
2618 - 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2619 - );
2620 - }
2621 -
2622 - return array(
2623 - 'success' => true,
2624 - 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2625 - );
2626 -}
2627 -
2628 -
2629 -
2630 -
2631 -
2632 -public function mxchat_create_activation_page() {
2633 - $license_status = get_option('mxchat_license_status', 'inactive');
2634 - $license_error = get_option('mxchat_license_error', '');
2635 - ?>
2636 - <div class="wrap mxchat-admin-activation">
2637 - <div class="mxchat-pro-hero">
2638 - <h1 class="pro-title">
2639 - <span class="pro-gradient-text">Activate</span> MxChat Pro
2640 - </h1>
2641 - <p class="pro-subtitle">
2642 - <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2643 - </p>
2644 - </div>
2645 -
2646 - <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
2647 - <div class="error notice">
2648 - <p><?php echo esc_html($license_error); ?></p>
2649 - </div>
2650 - <?php endif; ?>
2651 -
2652 - <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2653 - <div class="mxchat-pro-form-container">
2654 - <table class="form-table">
2655 - <tr valign="top">
2656 - <th scope="row"><?php esc_html_e('Email Address', 'mxchat'); ?></th>
2657 - <td>
2658 - <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text mxchat-pro-input" required />
2659 - </td>
2660 - </tr>
2661 - <tr valign="top">
2662 - <th scope="row"><?php esc_html_e('Activation Key', 'mxchat'); ?></th>
2663 - <td>
2664 - <input type="text" id="mxchat_activation_key" name="mxchat_activation_key" value="<?php echo esc_attr(get_option('mxchat_activation_key')); ?>" class="regular-text mxchat-pro-input" required />
2665 - </td>
2666 - </tr>
2667 - </table>
2668 - <?php if ($license_status !== 'active'): ?>
2669 - <div class="mxchat-pro-button-container">
2670 - <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2671 - <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
2672 - </div>
2673 - <?php endif; ?>
2674 - </div>
2675 - </form>
2676 - <!-- License Status Display -->
2677 - <div class="mxchat-pro-status">
2678 - <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2679 - <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2680 - <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2681 - </span>
2682 - </h3>
2683 - </div>
2684 -
2685 - </div>
2686 - <?php
2687 -}
2688 -
2689 -public function mxchat_intents_page_html() {
2690 - if (!current_user_can('manage_options')) {
2691 - return;
2692 - }
2693 -
2694 - // Keep existing data fetching logic
2695 - global $wpdb;
2696 - $table_name = $wpdb->prefix . 'mxchat_intents';
2697 - $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
2698 - $per_page = 20;
2699 - $offset = ($page - 1) * $per_page;
2700 -
2701 - // Success message
2702 - if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2703 - echo '<div class="notice notice-success is-dismissible"><p>' .
2704 - esc_html__('Intent updated successfully.', 'mxchat') .
2705 - '</p></div>';
2706 - }
2707 -
2708 - // Filtering logic
2709 - $where = '1=1';
2710 - $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
2711 - $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
2712 -
2713 - if ($search_term) {
2714 - $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
2715 - $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
2716 - $search_term_like, $search_term_like);
2717 - }
2718 -
2719 - if ($callback_filter) {
2720 - $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
2721 - }
2722 -
2723 - // Pagination
2724 - $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2725 - $total_pages = ceil($total_intents / $per_page);
2726 -
2727 - // Get intents
2728 - $intents = $wpdb->get_results($wpdb->prepare(
2729 - "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2730 - $per_page, $offset
2731 - ));
2732 -
2733 - // Get callbacks
2734 - $available_callbacks = $this->mxchat_get_available_callbacks();
2735 -
2736 - ?>
2737 - <div class="wrap mxchat-wrapper">
2738 - <!-- Hero Section -->
2739 - <div class="mxchat-hero">
2740 - <h1 class="mxchat-main-title">
2741 - <span class="mxchat-gradient-text">Intent</span> Manager
2742 - </h1>
2743 - <p class="mxchat-hero-subtitle">
2744 - <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
2745 - </p>
2746 - </div>
2747 -
2748 - <div class="mxchat-content">
2749 - <!-- Add Intent Card -->
2750 - <div class="mxchat-card">
2751 - <div class="mxchat-card-header">
2752 - <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2753 - </div>
2754 -
2755 - <div class="mxchat-intent-documentation">
2756 - <p>
2757 - <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2758 - <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2759 - <?php esc_html_e('documentation', 'mxchat'); ?>
2760 - </a>
2761 - <?php esc_html_e(' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat'); ?>
2762 - </p>
2763 - </div>
2764 -
2765 - <form id="mxchat-add-intent-form" method="post"
2766 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2767 - <input type="hidden" name="action" value="mxchat_add_intent">
2768 - <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2769 -
2770 - <div class="mxchat-form-group">
2771 - <label for="intent_label">
2772 - <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2773 - </label>
2774 - <input name="intent_label" type="text" id="intent_label" required
2775 - class="mxchat-intent-input"
2776 - placeholder="<?php esc_attr_e('Example Email Capture: Newsletter Signup', 'mxchat'); ?>">
2777 - </div>
2778 -
2779 - <div class="mxchat-form-group">
2780 - <label for="phrases">
2781 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2782 - </label>
2783 - <textarea name="phrases" id="phrases" rows="5" required
2784 - class="mxchat-intent-textarea"
2785 - placeholder="<?php esc_attr_e('Example Email Capture: sign me up, subscribe me, I want to join, add me to the newsletter, send me updates, keep me informed', 'mxchat'); ?>"></textarea>
2786 - </div>
2787 -
2788 - <div class="mxchat-form-group">
2789 - <label for="callback_function">
2790 - <?php esc_html_e('Callback Function', 'mxchat'); ?>
2791 - </label>
2792 - <select name="callback_function" id="callback_function"
2793 - class="mxchat-intent-select" required>
2794 - <option value="">
2795 - <?php esc_html_e('Select a Callback', 'mxchat'); ?>
2796 - </option>
2797 - <?php
2798 - $groups = $this->mxchat_get_available_callbacks(true);
2799 - foreach ($groups as $group_label => $group_callbacks) :
2800 - echo '<optgroup label="' . esc_attr($group_label) . '">';
2801 - foreach ($group_callbacks as $function => $data) :
2802 - $label = $data['label'];
2803 - $pro_only = $data['pro_only'];
2804 - $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2805 - $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2806 - ?>
2807 - <option value="<?php echo esc_attr($function); ?>"
2808 - <?php echo $disabled; ?>>
2809 - <?php echo esc_html($label . $label_suffix); ?>
2810 - </option>
2811 - <?php
2812 - endforeach;
2813 - echo '</optgroup>';
2814 - endforeach;
2815 - ?>
2816 - </select>
2817 - </div>
2818 -
2819 - <button type="submit" class="mxchat-button-primary">
2820 - <?php esc_html_e('Add Intent', 'mxchat'); ?>
2821 - </button>
2822 - </form>
2823 - </div>
2824 -
2825 - <!-- Manage Intents Card -->
2826 - <div class="mxchat-card">
2827 - <div class="mxchat-card-header">
2828 - <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2829 - <div class="mxchat-header-actions">
2830 - <form method="get" class="mxchat-search-form">
2831 - <input type="hidden" name="page" value="mxchat-intents">
2832 - <div class="mxchat-search-group">
2833 - <span class="dashicons dashicons-search"></span>
2834 - <input type="text" name="s"
2835 - placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2836 - value="<?php echo esc_attr($search_term); ?>">
2837 - <select name="callback_filter" class="mxchat-intent-filter">
2838 - <option value="">
2839 - <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2840 - </option>
2841 - <?php foreach ($available_callbacks as $function => $callback_data) :
2842 - $label = $callback_data['label']; ?>
2843 - <option value="<?php echo esc_attr($function); ?>"
2844 - <?php selected($callback_filter, $function); ?>>
2845 - <?php echo esc_html($label); ?>
2846 - </option>
2847 - <?php endforeach; ?>
2848 - </select>
2849 - <button type="submit" class="mxchat-button-secondary">
2850 - <?php esc_html_e('Filter', 'mxchat'); ?>
2851 - </button>
2852 - </div>
2853 - </form>
2854 - </div>
2855 - </div>
2856 -
2857 - <div class="mxchat-table-wrapper">
2858 - <table class="mxchat-records-table">
2859 - <thead>
2860 - <tr>
2861 - <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2862 - <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2863 - <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2864 - <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2865 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2866 - </tr>
2867 - </thead>
2868 - <tbody>
2869 - <?php if ($intents) :
2870 - foreach ($intents as $intent) :
2871 - $callback_function = $intent->callback_function;
2872 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2873 - ? $available_callbacks[$callback_function]['label']
2874 - : $callback_function;
2875 - $threshold_value = isset($intent->similarity_threshold)
2876 - ? round($intent->similarity_threshold * 100)
2877 - : 85;
2878 -
2879 - // Check if this is a form intent (its intent_label starts with "Form ")
2880 - $is_form_intent = strpos($intent->intent_label, 'Form ') === 0;
2881 - ?>
2882 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2883 - <td>
2884 - <?php
2885 - if ($is_form_intent) {
2886 - // Attempt to extract the form ID from the intent_label.
2887 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2888 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2889 - if ($form_id) {
2890 - global $wpdb;
2891 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2892 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2893 - if ($form && !empty($form->title)) {
2894 - echo esc_html($form->title);
2895 - } else {
2896 - echo esc_html($intent->intent_label);
2897 - }
2898 - } else {
2899 - echo esc_html($intent->intent_label);
2900 - }
2901 - echo ' <span class="mxchat-badge">Form Intent</span>';
2902 - } else {
2903 - echo esc_html($intent->intent_label);
2904 - }
2905 - ?>
2906 - </td>
2907 - <td class="mxchat-content-cell">
2908 - <?php echo esc_html($intent->phrases); ?>
2909 - </td>
2910 - <td><?php echo esc_html($callback_label); ?></td>
2911 - <td>
2912 - <!-- Similarity threshold adjustment (shown for all intents) -->
2913 - <form method="post"
2914 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2915 - class="mxchat-threshold-form">
2916 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2917 - <input type="hidden" name="action"
2918 - value="mxchat_update_intent_threshold">
2919 - <input type="hidden" name="intent_id"
2920 - value="<?php echo esc_attr($intent->id); ?>">
2921 - <div class="mxchat-slider-group">
2922 - <input type="range"
2923 - name="intent_threshold"
2924 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2925 - min="70"
2926 - max="95"
2927 - value="<?php echo esc_attr($threshold_value); ?>"
2928 - class="mxchat-intent-slider"
2929 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2930 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2931 - class="mxchat-intent-output">
2932 - <?php echo esc_html($threshold_value); ?>%
2933 - </output>
2934 - </div>
2935 - <button type="submit" class="mxchat-button-icon">
2936 - <span class="dashicons dashicons-saved"></span>
2937 - </button>
2938 - </form>
2939 - </td>
2940 - <td class="mxchat-actions-cell">
2941 - <?php if (!$is_form_intent) : ?>
2942 - <!-- Standard intents: allow edit and delete -->
2943 - <button type="button"
2944 - class="mxchat-button-icon mxchat-edit-button"
2945 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2946 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2947 - <span class="dashicons dashicons-edit"></span>
2948 - </button>
2949 - <form method="post"
2950 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2951 - class="mxchat-delete-form"
2952 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2953 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2954 - <input type="hidden" name="action" value="mxchat_delete_intent">
2955 - <input type="hidden" name="intent_id"
2956 - value="<?php echo esc_attr($intent->id); ?>">
2957 - <button type="submit" class="mxchat-button-icon">
2958 - <span class="dashicons dashicons-trash"></span>
2959 - </button>
2960 - </form>
2961 - <?php else : ?>
2962 - <!-- Form intents: show manage in forms button -->
2963 - <?php
2964 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2965 - $form_id = isset($matches[1]) ? $matches[1] : '';
2966 - ?>
2967 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2968 - class="mxchat-button-secondary">
2969 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2970 - </a>
2971 - <?php endif; ?>
2972 - </td>
2973 - </tr>
2974 - <?php endforeach;
2975 - else : ?>
2976 - <tr>
2977 - <td colspan="5" class="mxchat-no-records">
2978 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2979 - </td>
2980 - </tr>
2981 - <?php endif; ?>
2982 - </tbody>
2983 - </table>
2984 - </div>
2985 -
2986 - <?php if ($total_pages > 1) : ?>
2987 - <div class="mxchat-pagination">
2988 - <?php
2989 - echo paginate_links(array(
2990 - 'base' => add_query_arg('paged', '%#%'),
2991 - 'format' => '',
2992 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
2993 - 'next_text' => __('Next &raquo;', 'mxchat'),
2994 - 'total' => $total_pages,
2995 - 'current' => $page
2996 - ));
2997 - ?>
2998 - </div>
2999 - <?php endif; ?>
3000 - </div>
3001 - </div>
3002 -
3003 - <!-- Edit Modal -->
3004 -<div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
3005 - <div class="mxchat-modal-content">
3006 - <span class="mxchat-modal-close">&times;</span>
3007 - <div class="mxchat-card-header">
3008 - <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
3009 - </div>
3010 -
3011 - <form id="mxchat-edit-intent-form" method="post"
3012 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3013 - <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
3014 - <input type="hidden" name="action" value="mxchat_edit_intent">
3015 - <input type="hidden" name="intent_id" id="edit_intent_id">
3016 -
3017 - <div class="mxchat-form-group">
3018 - <label for="edit_phrases">
3019 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
3020 - </label>
3021 - <textarea name="phrases"
3022 - id="edit_phrases"
3023 - rows="5"
3024 - class="mxchat-intent-textarea"
3025 - required></textarea>
3026 - </div>
3027 -
3028 - <div class="mxchat-modal-actions">
3029 - <button type="submit" class="mxchat-button-primary">
3030 - <?php esc_html_e('Update Phrases', 'mxchat'); ?>
3031 - </button>
3032 - <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3033 - <?php esc_html_e('Cancel', 'mxchat'); ?>
3034 - </button>
3035 - </div>
3036 - </form>
3037 - </div>
3038 -</div>
3039 -
3040 - </div><!-- .mxchat-content -->
3041 - </div><!-- .mxchat-wrapper -->
3042 -
3043 - <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
3044 - <div class="mxchat-intent-loading-spinner"></div>
3045 - <div class="mxchat-intent-loading-text">
3046 - <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
3047 - </div>
3048 - </div>
3049 -<?php
3050 -}
3051 -
3052 -
3053 -
3054 -/**
3055 - * Handle editing of intent phrases
3056 - *
3057 - * @since 1.0.0
3058 - * @return void
3059 - */
3060 -public function handle_edit_intent() {
3061 - // Verify nonce and user capabilities
3062 - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
3063 - wp_die(esc_html__('Security check failed.', 'mxchat'));
3064 - }
3065 -
3066 - if (!current_user_can('manage_options')) {
3067 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
3068 - }
3069 -
3070 - // Validate and sanitize input
3071 - $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
3072 - if (!$intent_id) {
3073 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
3074 - }
3075 -
3076 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
3077 - if (empty($phrases_input)) {
3078 - wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
3079 - }
3080 -
3081 - // Process phrases the same way as in intent creation
3082 - $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
3083 -
3084 - if (empty($phrases_array)) {
3085 - wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
3086 - }
3087 -
3088 - // Generate embeddings and combine them
3089 - $vectors = [];
3090 - foreach ($phrases_array as $phrase) {
3091 - $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3092 - if (is_array($embedding_vector)) {
3093 - $vectors[] = $embedding_vector;
3094 - } else {
3095 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
3096 - }
3097 - }
3098 -
3099 - if (empty($vectors)) {
3100 - wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3101 - }
3102 -
3103 - // Create combined vector just like in intent creation
3104 - $combined_vector = $this->mxchat_average_vectors($vectors);
3105 - $serialized_vector = maybe_serialize($combined_vector);
3106 -
3107 - global $wpdb;
3108 - $table_name = $wpdb->prefix . 'mxchat_intents';
3109 -
3110 - // Update the intent with both new phrases and the combined vector
3111 - $result = $wpdb->update(
3112 - $table_name,
3113 - array(
3114 - 'phrases' => implode(', ', $phrases_array),
3115 - 'embedding_vector' => $serialized_vector
3116 - ),
3117 - array('id' => $intent_id),
3118 - array('%s', '%s'),
3119 - array('%d')
3120 - );
3121 -
3122 - if (false === $result) {
3123 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
3124 - }
3125 -
3126 - // Redirect back to the intents page with a success message
3127 - $redirect_url = add_query_arg(
3128 - array(
3129 - 'page' => 'mxchat-intents',
3130 - 'updated' => 'true'
3131 - ),
3132 - admin_url('admin.php')
3133 - );
3134 -
3135 - wp_safe_redirect($redirect_url);
3136 - exit;
3137 -}
3138 -public function mxchat_handle_update_intent_threshold() {
3139 - if ( ! current_user_can( 'manage_options' ) ) {
3140 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3141 - }
3142 -
3143 - check_admin_referer('mxchat_update_intent_threshold_nonce');
3144 -
3145 - if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
3146 - global $wpdb;
3147 - $table_name = $wpdb->prefix . 'mxchat_intents';
3148 - $intent_id = intval($_POST['intent_id']);
3149 - $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
3150 - $similarity_threshold = $threshold_percentage / 100;
3151 -
3152 - $wpdb->update(
3153 - $table_name,
3154 - ['similarity_threshold' => $similarity_threshold],
3155 - ['id' => $intent_id],
3156 - ['%f'],
3157 - ['%d']
3158 - );
3159 - }
3160 -
3161 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3162 - exit;
3163 -}
3164 -
3165 -public function mxchat_handle_add_intent() {
3166 - if ( ! current_user_can( 'manage_options' ) ) {
3167 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3168 - }
3169 -
3170 - check_admin_referer('mxchat_add_intent_nonce');
3171 -
3172 - global $wpdb;
3173 - $table_name = $wpdb->prefix . 'mxchat_intents';
3174 -
3175 - $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
3176 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
3177 - $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
3178 - $default_threshold = 0.85;
3179 -
3180 - if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
3181 - wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
3182 - }
3183 -
3184 - $available_callbacks = $this->mxchat_get_available_callbacks();
3185 -
3186 - if (!array_key_exists($callback_function, $available_callbacks)) {
3187 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
3188 - }
3189 -
3190 - $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
3191 - if ($is_pro_only && !$this->is_activated) {
3192 - wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
3193 - }
3194 -
3195 - $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
3196 -
3197 - if (empty($phrases_array)) {
3198 - wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
3199 - }
3200 -
3201 - $vectors = [];
3202 - foreach ($phrases_array as $phrase) {
3203 - $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3204 - if (is_array($embedding_vector)) {
3205 - $vectors[] = $embedding_vector;
3206 - } else {
3207 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
3208 - }
3209 - }
3210 -
3211 - if (!empty($vectors)) {
3212 - $combined_vector = $this->mxchat_average_vectors($vectors);
3213 - $serialized_vector = maybe_serialize($combined_vector);
3214 -
3215 - $result = $wpdb->insert($table_name, [
3216 - 'intent_label' => $intent_label,
3217 - 'phrases' => implode(', ', $phrases_array),
3218 - 'embedding_vector' => $serialized_vector,
3219 - 'callback_function' => $callback_function,
3220 - 'similarity_threshold' => $default_threshold,
3221 - ]);
3222 -
3223 - if ($result === false) {
3224 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3225 - }
3226 - } else {
3227 - wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
3228 - }
3229 -
3230 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3231 - exit;
3232 -}
3233 -
3234 -
3235 -
3236 -
3237 -
3238 -
3239 -private function mxchat_get_available_callbacks($grouped = false) {
3240 - $callbacks = [
3241 - 'mxchat_handle_email_capture' => [
3242 - 'label' => __('Email Capture', 'mxchat'),
3243 - 'pro_only' => false,
3244 - 'group' => __('Customer Engagement', 'mxchat'),
3245 - ],
3246 - 'mxchat_generate_image' => [
3247 - 'label' => __('Generate Image', 'mxchat'),
3248 - 'pro_only' => true,
3249 - 'group' => __('Other Features', 'mxchat'),
3250 - ],
3251 - 'mxchat_handle_search_request' => [
3252 - 'label' => __('Brave Web Search', 'mxchat'),
3253 - 'pro_only' => false,
3254 - 'group' => __('Search Features', 'mxchat'),
3255 - ],
3256 - 'mxchat_handle_image_search_request' => [
3257 - 'label' => __('Brave Image Search', 'mxchat'),
3258 - 'pro_only' => false,
3259 - 'group' => __('Search Features', 'mxchat'),
3260 - ],
3261 - 'mxchat_handle_pdf_discussion' => [
3262 - 'label' => __('Chat with PDF', 'mxchat'),
3263 - 'pro_only' => true,
3264 - 'group' => __('Other Features', 'mxchat'),
3265 - ],
3266 - 'mxchat_live_agent_handover' => [
3267 - 'label' => __('Live Agent', 'mxchat'),
3268 - 'pro_only' => true,
3269 - 'group' => __('Customer Engagement', 'mxchat'),
3270 - ],
3271 - 'mxchat_handle_switch_to_chatbot_intent' => [
3272 - 'label' => __('Back to Chatbot', 'mxchat'),
3273 - 'pro_only' => true,
3274 - 'group' => __('Customer Engagement', 'mxchat'),
3275 - ],
3276 - ];
3277 -
3278 - // Allow other plugins to add their callbacks
3279 - $callbacks = apply_filters('mxchat_available_callbacks', $callbacks);
3280 -
3281 - // Return grouped structure if requested
3282 - if ($grouped) {
3283 - $grouped_callbacks = [];
3284 - foreach ($callbacks as $key => $data) {
3285 - $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3286 - $grouped_callbacks[$group_label][$key] = [
3287 - 'label' => $data['label'],
3288 - 'pro_only' => $data['pro_only'],
3289 - ];
3290 - }
3291 - return $grouped_callbacks;
3292 - }
3293 - return $callbacks;
3294 -}
3295 -
3296 -
3297 -private function mxchat_average_vectors($vectors) {
3298 - $vector_length = count($vectors[0]);
3299 - $sum_vector = array_fill(0, $vector_length, 0);
3300 -
3301 - foreach ($vectors as $vector) {
3302 - for ($i = 0; $i < $vector_length; $i++) {
3303 - $sum_vector[$i] += $vector[$i];
3304 - }
3305 - }
3306 -
3307 - // Divide each component by the number of vectors to get the average
3308 - $num_vectors = count($vectors);
3309 - for ($i = 0; $i < $vector_length; $i++) {
3310 - $sum_vector[$i] /= $num_vectors;
3311 - }
3312 -
3313 - return $sum_vector;
3314 -}
3315 -
3316 -public function mxchat_handle_delete_intent() {
3317 - if ( ! current_user_can( 'manage_options' ) ) {
3318 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
3319 - }
3320 -
3321 - check_admin_referer('mxchat_delete_intent_nonce');
3322 -
3323 - if (isset($_POST['intent_id'])) {
3324 - global $wpdb;
3325 - $table_name = $wpdb->prefix . 'mxchat_intents';
3326 - $intent_id = intval($_POST['intent_id']);
3327 -
3328 - $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3329 - }
3330 -
3331 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3332 - exit;
3333 -}
3334 -
3335 -
3336 -public function mxchat_page_init() {
3337 - // Register settings
3338 - register_setting(
3339 - 'mxchat_option_group',
3340 - 'mxchat_options',
3341 - array($this, 'mxchat_sanitize')
3342 - );
3343 -
3344 - register_setting(
3345 - 'mxchat_option_group',
3346 - 'mxchat_similarity_threshold',
3347 - array(
3348 - 'type' => 'number',
3349 - 'sanitize_callback' => function($value) {
3350 - $value = absint($value); // Ensure it's an integer
3351 - return min(max($value, 70), 85); // Enforce range
3352 - },
3353 - 'default' => 80,
3354 - )
3355 - );
3356 -
3357 - // Chatbot Settings Section
3358 - add_settings_section(
3359 - 'mxchat_chatbot_section',
3360 - esc_html__('Chatbot Settings', 'mxchat'),
3361 - null,
3362 - 'mxchat-chatbot'
3363 - );
3364 -
3365 - // Similarity Threshold Slider
3366 - add_settings_field(
3367 - 'similarity_threshold', // Field ID
3368 - esc_html__('Similarity Threshold', 'mxchat'), // Field title
3369 - array($this, 'mxchat_similarity_threshold_callback'), // Callback function
3370 - 'mxchat-chatbot', // Page
3371 - 'mxchat_chatbot_section' // Section
3372 - );
3373 -
3374 - add_settings_field(
3375 - 'append_to_body',
3376 - esc_html__('Auto-Display Chatbot', 'mxchat'),
3377 - array($this, 'mxchat_append_to_body_callback'),
3378 - 'mxchat-chatbot',
3379 - 'mxchat_chatbot_section'
3380 - );
3381 -
3382 -
3383 - // Existing fields...
3384 - add_settings_field(
3385 - 'api_key',
3386 - esc_html__('OpenAI API Key', 'mxchat'),
3387 - array($this, 'api_key_callback'),
3388 - 'mxchat-chatbot',
3389 - 'mxchat_chatbot_section'
3390 - );
3391 -
3392 - add_settings_field(
3393 - 'xai_api_key',
3394 - esc_html__('X.AI API Key', 'mxchat'),
3395 - array($this, 'xai_api_key_callback'),
3396 - 'mxchat-chatbot',
3397 - 'mxchat_chatbot_section'
3398 - );
3399 -
3400 - add_settings_field(
3401 - 'claude_api_key',
3402 - esc_html__('Claude API Key', 'mxchat'),
3403 - array($this, 'claude_api_key_callback'),
3404 - 'mxchat-chatbot',
3405 - 'mxchat_chatbot_section'
3406 - );
3407 -
3408 - add_settings_field(
3409 - 'deepseek_api_key',
3410 - esc_html__('DeepSeek API Key', 'mxchat'),
3411 - array($this, 'deepseek_api_key_callback'),
3412 - 'mxchat-chatbot',
3413 - 'mxchat_chatbot_section'
3414 - );
3415 -
3416 - add_settings_field(
3417 - 'voyage_api_key',
3418 - esc_html__('Voyage AI API Key', 'mxchat'),
3419 - array($this, 'voyage_api_key_callback'),
3420 - 'mxchat-chatbot',
3421 - 'mxchat_chatbot_section'
3422 - );
3423 -
3424 -
3425 - add_settings_field(
3426 - 'model',
3427 - esc_html__('Chat Model', 'mxchat'),
3428 - array($this, 'mxchat_model_callback'),
3429 - 'mxchat-chatbot',
3430 - 'mxchat_chatbot_section'
3431 - );
3432 -
3433 - // Add the settings field
3434 - add_settings_field(
3435 - 'embedding_model',
3436 - esc_html__('Embedding Model', 'mxchat'),
3437 - array($this, 'embedding_model_callback'),
3438 - 'mxchat-chatbot',
3439 - 'mxchat_chatbot_section'
3440 - );
3441 -
3442 - add_settings_field(
3443 - 'system_prompt_instructions',
3444 - esc_html__('AI Instructions (Behavior)', 'mxchat'),
3445 - array($this, 'system_prompt_instructions_callback'),
3446 - 'mxchat-chatbot',
3447 - 'mxchat_chatbot_section'
3448 - );
3449 -
3450 -
3451 - add_settings_field(
3452 - 'top_bar_title',
3453 - esc_html__('Top Bar Title', 'mxchat'),
3454 - array($this, 'mxchat_top_bar_title_callback'),
3455 - 'mxchat-chatbot',
3456 - 'mxchat_chatbot_section'
3457 - );
3458 -
3459 - add_settings_field(
3460 - 'enable_email_block',
3461 - esc_html__('Require Email To Chat', 'mxchat'),
3462 - array($this, 'enable_email_block_callback'),
3463 - 'mxchat-chatbot',
3464 - 'mxchat_chatbot_section'
3465 - );
3466 -
3467 - add_settings_field(
3468 - 'email_blocker_header_content',
3469 - esc_html__('Require Email Chat Content', 'mxchat'),
3470 - array($this, 'email_blocker_header_content_callback'),
3471 - 'mxchat-chatbot',
3472 - 'mxchat_chatbot_section'
3473 - );
3474 -
3475 - add_settings_field(
3476 - 'email_blocker_button_text',
3477 - esc_html__('Require Email Chat Button Text', 'mxchat'),
3478 - [$this, 'email_blocker_button_text_callback'],
3479 - 'mxchat-chatbot',
3480 - 'mxchat_chatbot_section'
3481 - );
3482 -
3483 - add_settings_field(
3484 - 'intro_message',
3485 - esc_html__('Introductory Message', 'mxchat'),
3486 - array($this, 'mxchat_intro_message_callback'),
3487 - 'mxchat-chatbot',
3488 - 'mxchat_chatbot_section'
3489 - );
3490 -
3491 - add_settings_field(
3492 - 'input_copy',
3493 - esc_html__('Input Copy', 'mxchat'),
3494 - array($this, 'mxchat_input_copy_callback'),
3495 - 'mxchat-chatbot',
3496 - 'mxchat_chatbot_section'
3497 - );
3498 -
3499 - add_settings_field(
3500 - 'rate_limit_logged_out',
3501 - __('Rate Limit for Logged-out Users', 'mxchat'),
3502 - array($this, 'mxchat_rate_limit_logged_out_callback'),
3503 - 'mxchat-chatbot',
3504 - 'mxchat_chatbot_section'
3505 - );
3506 -
3507 - add_settings_field(
3508 - 'rate_limit_roles',
3509 - __('Rate Limits by User Role', 'mxchat'),
3510 - array($this, 'mxchat_rate_limit_roles_callback'),
3511 - 'mxchat-chatbot',
3512 - 'mxchat_chatbot_section'
3513 - );
3514 -
3515 - add_settings_field(
3516 - 'rate_limit_message',
3517 - esc_html__('Rate Limit Message', 'mxchat'),
3518 - array($this, 'mxchat_rate_limit_message_callback'),
3519 - 'mxchat-chatbot',
3520 - 'mxchat_chatbot_section'
3521 - );
3522 -
3523 - add_settings_field(
3524 - 'pre_chat_message',
3525 - esc_html__('Chat Teaser Pop-up', 'mxchat'),
3526 - array($this, 'mxchat_pre_chat_message_callback'),
3527 - 'mxchat-chatbot',
3528 - 'mxchat_chatbot_section'
3529 - );
3530 -
3531 - add_settings_field(
3532 - 'privacy_toggle',
3533 - esc_html__('Toggle Privacy Notice', 'mxchat'),
3534 - array($this, 'mxchat_privacy_toggle_callback'),
3535 - 'mxchat-chatbot',
3536 - 'mxchat_chatbot_section'
3537 - );
3538 -
3539 - add_settings_field(
3540 - 'complianz_toggle',
3541 - esc_html__('Enable Complianz', 'mxchat'),
3542 - array($this, 'mxchat_complianz_toggle_callback'),
3543 - 'mxchat-chatbot',
3544 - 'mxchat_chatbot_section'
3545 - );
3546 -
3547 - add_settings_field(
3548 - 'link_target_toggle',
3549 - esc_html__('Open Links in a New Tab', 'mxchat'),
3550 - array($this, 'mxchat_link_target_toggle_callback'),
3551 - 'mxchat-chatbot',
3552 - 'mxchat_chatbot_section'
3553 - );
3554 -
3555 - add_settings_field(
3556 - 'chat_persistence_toggle',
3557 - esc_html__('Enable Chat Persistence', 'mxchat'),
3558 - array($this, 'mxchat_chat_persistence_toggle_callback'),
3559 - 'mxchat-chatbot',
3560 - 'mxchat_chatbot_section'
3561 - );
3562 -
3563 - add_settings_field(
3564 - 'popular_question_1',
3565 - esc_html__('Quick Question 1', 'mxchat'),
3566 - array($this, 'mxchat_popular_question_1_callback'),
3567 - 'mxchat-chatbot',
3568 - 'mxchat_chatbot_section'
3569 - );
3570 -
3571 - add_settings_field(
3572 - 'popular_question_2',
3573 - esc_html__('Quick Question 2', 'mxchat'),
3574 - array($this, 'mxchat_popular_question_2_callback'),
3575 - 'mxchat-chatbot',
3576 - 'mxchat_chatbot_section'
3577 - );
3578 -
3579 - add_settings_field(
3580 - 'popular_question_3',
3581 - esc_html__('Quick Question 3', 'mxchat'),
3582 - array($this, 'mxchat_popular_question_3_callback'),
3583 - 'mxchat-chatbot',
3584 - 'mxchat_chatbot_section'
3585 - );
3586 -
3587 - add_settings_field(
3588 - 'additional_popular_questions',
3589 - esc_html__('Additional Quick Questions', 'mxchat'),
3590 - array($this, 'mxchat_additional_popular_questions_callback'),
3591 - 'mxchat-chatbot',
3592 - 'mxchat_chatbot_section'
3593 - );
3594 -
3595 -
3596 - // Loops Settings Section
3597 - add_settings_section(
3598 - 'mxchat_loops_section',
3599 - esc_html__('Loops Settings', 'mxchat'),
3600 - null,
3601 - 'mxchat-embed'
3602 - );
3603 -
3604 - // Loops Settings Fields
3605 - add_settings_field(
3606 - 'loops_api_key',
3607 - esc_html__('Loops API Key', 'mxchat'),
3608 - array($this, 'mxchat_loops_api_key_callback'),
3609 - 'mxchat-embed',
3610 - 'mxchat_loops_section'
3611 - );
3612 -
3613 - add_settings_field(
3614 - 'loops_mailing_list',
3615 - esc_html__('Loops Mailing List', 'mxchat'),
3616 - array($this, 'mxchat_loops_mailing_list_callback'),
3617 - 'mxchat-embed',
3618 - 'mxchat_loops_section'
3619 - );
3620 -
3621 - add_settings_field(
3622 - 'triggered_phrase_response',
3623 - esc_html__('Triggered Phrase Response', 'mxchat'),
3624 - array($this, 'mxchat_triggered_phrase_response_callback'),
3625 - 'mxchat-embed',
3626 - 'mxchat_loops_section'
3627 - );
3628 -
3629 - add_settings_field(
3630 - 'email_capture_response',
3631 - esc_html__('Email Capture Response', 'mxchat'),
3632 - array($this, 'mxchat_email_capture_response_callback'),
3633 - 'mxchat-embed',
3634 - 'mxchat_loops_section'
3635 - );
3636 -
3637 - // Brave Search Settings Fields
3638 - add_settings_section(
3639 - 'mxchat_brave_section',
3640 - __('Brave Search Settings', 'mxchat'),
3641 - array($this, 'mxchat_brave_section_callback'),
3642 - 'mxchat-embed'
3643 - );
3644 -
3645 - add_settings_field(
3646 - 'brave_api_key',
3647 - __('Brave API Key', 'mxchat'),
3648 - array($this, 'mxchat_brave_api_key_callback'),
3649 - 'mxchat-embed',
3650 - 'mxchat_brave_section'
3651 - );
3652 -
3653 - add_settings_field(
3654 - 'brave_image_count',
3655 - __('Number of Images to Return', 'mxchat'),
3656 - array($this, 'mxchat_brave_image_count_callback'),
3657 - 'mxchat-embed',
3658 - 'mxchat_brave_section'
3659 - );
3660 -
3661 - add_settings_field(
3662 - 'brave_safe_search',
3663 - __('Safe Search', 'mxchat'),
3664 - array($this, 'mxchat_brave_safe_search_callback'),
3665 - 'mxchat-embed',
3666 - 'mxchat_brave_section'
3667 - );
3668 -
3669 - add_settings_field(
3670 - 'brave_news_count',
3671 - __('Number of News Articles', 'mxchat'),
3672 - array($this, 'mxchat_brave_news_count_callback'),
3673 - 'mxchat-embed',
3674 - 'mxchat_brave_section'
3675 - );
3676 -
3677 - add_settings_field(
3678 - 'brave_country',
3679 - __('Country', 'mxchat'),
3680 - array($this, 'mxchat_brave_country_callback'),
3681 - 'mxchat-embed',
3682 - 'mxchat_brave_section'
3683 - );
3684 -
3685 - add_settings_field(
3686 - 'brave_language',
3687 - __('Language', 'mxchat'),
3688 - array($this, 'mxchat_brave_language_callback'),
3689 - 'mxchat-embed',
3690 - 'mxchat_brave_section'
3691 - );
3692 -
3693 - // Chat with PDF Intent Settings Fields
3694 - add_settings_section(
3695 - 'mxchat_pdf_intent_section',
3696 - __('Toolbar Settings & Intents', 'mxchat'),
3697 - array($this, 'mxchat_pdf_intent_section_callback'),
3698 - 'mxchat-embed'
3699 - );
3700 -
3701 - add_settings_field(
3702 - 'chat_toolbar_toggle',
3703 - __('Show Chat Toolbar', 'mxchat'),
3704 - array($this, 'mxchat_chat_toolbar_toggle_callback'),
3705 - 'mxchat-embed',
3706 - 'mxchat_pdf_intent_section'
3707 - );
3708 -
3709 - add_settings_field(
3710 - 'pdf_intent_trigger_text',
3711 - __('Intent Trigger Text', 'mxchat'),
3712 - array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3713 - 'mxchat-embed',
3714 - 'mxchat_pdf_intent_section'
3715 - );
3716 -
3717 - add_settings_field(
3718 - 'pdf_intent_success_text',
3719 - __('Success Text', 'mxchat'),
3720 - array($this, 'mxchat_pdf_intent_success_text_callback'),
3721 - 'mxchat-embed',
3722 - 'mxchat_pdf_intent_section'
3723 - );
3724 -
3725 - add_settings_field(
3726 - 'pdf_intent_error_text',
3727 - __('Error Text', 'mxchat'),
3728 - array($this, 'mxchat_pdf_intent_error_text_callback'),
3729 - 'mxchat-embed',
3730 - 'mxchat_pdf_intent_section'
3731 - );
3732 -
3733 - // Add PDF Maximum Pages Field
3734 - add_settings_field(
3735 - 'pdf_max_pages',
3736 - __('Maximum Document Pages', 'mxchat'),
3737 - array($this, 'mxchat_pdf_max_pages_callback'),
3738 - 'mxchat-embed',
3739 - 'mxchat_pdf_intent_section'
3740 - );
3741 -
3742 - // Live Agent Settings Fields
3743 - add_settings_section(
3744 - 'mxchat_live_agent_section',
3745 - __('Live Agent Settings', 'mxchat'),
3746 - array($this, 'mxchat_live_agent_section_callback'),
3747 - 'mxchat-embed'
3748 - );
3749 -
3750 - // Live Agent Status Fields (add at top of live agent settings)
3751 - add_settings_field(
3752 - 'live_agent_status',
3753 - __('Live Agent Status', 'mxchat'),
3754 - array($this, 'mxchat_live_agent_status_callback'),
3755 - 'mxchat-embed',
3756 - 'mxchat_live_agent_section'
3757 - );
3758 -
3759 - add_settings_field(
3760 - 'live_agent_notification_message',
3761 - __('Notification Message', 'mxchat'),
3762 - array($this, 'mxchat_live_agent_notification_message_callback'),
3763 - 'mxchat-embed',
3764 - 'mxchat_live_agent_section'
3765 - );
3766 -
3767 - add_settings_field(
3768 - 'live_agent_away_message',
3769 - __('Away Message', 'mxchat'),
3770 - array($this, 'mxchat_live_agent_away_message_callback'),
3771 - 'mxchat-embed',
3772 - 'mxchat_live_agent_section'
3773 - );
3774 -
3775 - add_settings_field(
3776 - 'live_agent_webhook_url',
3777 - __('Slack Webhook URL', 'mxchat'),
3778 - array($this, 'mxchat_live_agent_webhook_url_callback'),
3779 - 'mxchat-embed',
3780 - 'mxchat_live_agent_section'
3781 - );
3782 -
3783 - add_settings_field(
3784 - 'live_agent_secret_key',
3785 - __('Slack Secret Key', 'mxchat'),
3786 - array($this, 'mxchat_live_agent_secret_key_callback'),
3787 - 'mxchat-embed',
3788 - 'mxchat_live_agent_section'
3789 - );
3790 -
3791 - // Live Agent Integration Fields
3792 - add_settings_field(
3793 - 'live_agent_bot_token',
3794 - __('Slack Bot OAuth Token', 'mxchat'),
3795 - array($this, 'mxchat_live_agent_bot_token_callback'),
3796 - 'mxchat-embed',
3797 - 'mxchat_live_agent_section'
3798 - );
3799 -
3800 -
3801 -
3802 -
3803 - // General Settings Section
3804 - add_settings_section(
3805 - 'mxchat_general_section',
3806 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
3807 - null,
3808 - 'mxchat-general'
3809 - );
3810 -}
3811 -
3812 -public function mxchat_prompts_page_init() {
3813 - // Register all settings as a single array
3814 - register_setting(
3815 - 'mxchat_prompts_options',
3816 - 'mxchat_prompts_options',
3817 - array(
3818 - 'type' => 'array',
3819 - 'description' => __('MXChat Knowledge Base Settings', 'mxchat'),
3820 - 'default' => array(
3821 - 'mxchat_auto_sync_posts' => 0,
3822 - 'mxchat_auto_sync_pages' => 0,
3823 - 'mxchat_use_pinecone' => 0,
3824 - 'mxchat_pinecone_api_key' => '',
3825 - 'mxchat_pinecone_environment' => '',
3826 - 'mxchat_pinecone_index' => '',
3827 - 'mxchat_pinecone_host' => '', // Add this line
3828 - ),
3829 - 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3830 - )
3831 - );
3832 -
3833 - // Add settings saved message
3834 - add_action('admin_notices', array($this, 'sync_settings_notice'));
3835 -}
3836 -
3837 -/**
3838 - * Sanitize all prompts options
3839 - *
3840 - * @param array $input The unsanitized options array
3841 - * @return array The sanitized options array
3842 - */
3843 -public function sanitize_prompts_options($input) {
3844 - // Log the incoming input.
3845 - //error_log('Sanitizing inputs: ' . print_r($input, true));
3846 -
3847 - $sanitized = array();
3848 -
3849 - // Boolean options
3850 - $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0;
3851 - $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0;
3852 -$sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0;
3853 -
3854 - // API Key: if less than 32 characters, flag as invalid.
3855 - $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
3856 - if (!empty($api_key) && strlen($api_key) < 32) {
3857 - add_settings_error(
3858 - 'mxchat_prompts_options',
3859 - 'invalid_api_key',
3860 - __('The Pinecone API key appears to be invalid. Please check your API key.', 'mxchat')
3861 - );
3862 - $existing_options = get_option('mxchat_prompts_options', array());
3863 - $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? '';
3864 - } else {
3865 - $sanitized['mxchat_pinecone_api_key'] = $api_key;
3866 - }
3867 -
3868 - // Environment and Index Name
3869 - $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
3870 - $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
3871 -
3872 - // Host: Remove protocol and validate format.
3873 - $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
3874 - $host = preg_replace('#^https?://#', '', $host);
3875 - //error_log('Host after removing protocol: ' . $host);
3876 - if (!empty($host)) {
3877 - if (!preg_match('/^[\w-]+\.svc\.[\w-]+\.pinecone\.io$/', $host)) {
3878 - add_settings_error(
3879 - 'mxchat_prompts_options',
3880 - 'invalid_host',
3881 - __('The Pinecone host appears to be invalid. It should look like "mxchat-vectors-zrmsquq.svc.aped-4627-b74a.pinecone.io"', 'mxchat')
3882 - );
3883 - $existing_options = get_option('mxchat_prompts_options', array());
3884 - $sanitized['mxchat_pinecone_host'] = $existing_options['mxchat_pinecone_host'] ?? '';
3885 - } else {
3886 - $sanitized['mxchat_pinecone_host'] = $host;
3887 - }
3888 - } else {
3889 - $sanitized['mxchat_pinecone_host'] = '';
3890 - }
3891 -
3892 - //error_log('Final sanitized array: ' . print_r($sanitized, true));
3893 -
3894 - return $sanitized;
3895 -}
3896 -
3897 -
3898 -public function sync_settings_notice() {
3899 - // Only show notice on our plugin page
3900 - if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3901 - return;
3902 - }
3903 -
3904 - // Check if settings were updated
3905 - if (isset($_GET['settings-updated'])) {
3906 -
3907 - ?>
3908 - <div class="notice notice-success is-dismissible">
3909 - <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
3910 - </div>
3911 - <?php
3912 -
3913 - }
3914 -}
3915 -// Add this sanitization function to your class
3916 -public function sanitize_sync_setting($input) {
3917 - return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3918 -}
3919 -
3920 -
3921 -
3922 -public function mxchat_handle_activate_license() {
3923 - // Check nonce
3924 - if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3925 - wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
3926 - return;
3927 - }
3928 -
3929 - // Verify user capabilities
3930 - if (!current_user_can('manage_options')) {
3931 - wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
3932 - return;
3933 - }
3934 -
3935 - $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3936 - $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
3937 -
3938 - if (empty($license_key) || empty($customer_email)) {
3939 - wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
3940 - return;
3941 - }
3942 -
3943 - $product_id = 'MxChatPRO';
3944 - $response = wp_remote_get(
3945 - add_query_arg(
3946 - array(
3947 - 'wc-api' => 'software-api',
3948 - 'request' => 'activation',
3949 - 'email' => $customer_email,
3950 - 'license_key' => $license_key,
3951 - 'product_id' => $product_id
3952 - ),
3953 - 'http://mxchat.ai/'
3954 - )
3955 - );
3956 -
3957 - if (is_wp_error($response)) {
3958 - wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message());
3959 - return;
3960 - }
3961 -
3962 - $body = wp_remote_retrieve_body($response);
3963 - $data = json_decode($body);
3964 -
3965 - if ($data && isset($data->activated) && $data->activated) {
3966 - update_option('mxchat_license_status', 'active');
3967 - update_option('mxchat_pro_email', $customer_email);
3968 - update_option('mxchat_activation_key', $license_key);
3969 - wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
3970 - } else {
3971 - $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
3972 - update_option('mxchat_license_status', 'inactive');
3973 - update_option('mxchat_license_error', $error_message);
3974 - wp_send_json_error($error_message);
3975 - }
3976 -}
3977 -
3978 -
3979 -public function mxchat_rate_limit_logged_out_callback() {
3980 - // Load the entire 'mxchat_options' array
3981 - $all_options = get_option('mxchat_options', []);
3982 -
3983 - // Retrieve the saved rate limit or use the default value
3984 - $default_rate_limit = '10';
3985 - $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
3986 -
3987 - // Define available rate limits
3988 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3989 -
3990 - // Output the dropdown
3991 - echo '<div class="pro-feature-wrapper active">';
3992 - echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3993 - foreach ($rate_limits as $limit) {
3994 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3995 - }
3996 - echo '</select>';
3997 - echo '<p class="description">' . esc_html__('Set the maximum number of messages a logged-out user can send within a 24-hour period.', 'mxchat') . '</p>';
3998 - echo '</div>';
3999 -}
4000 -
4001 -// Add this new callback function
4002 -public function mxchat_rate_limit_roles_callback() {
4003 - $all_options = get_option('mxchat_options', []);
4004 - $roles = wp_roles()->get_names();
4005 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
4006 -
4007 - echo '<div class="pro-feature-wrapper active mxchat-autosave-section">';
4008 -
4009 - foreach ($roles as $role_id => $role_name) {
4010 - $default_rate_limit = '100';
4011 - $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id])
4012 - ? $all_options['role_rate_limits'][$role_id]
4013 - : $default_rate_limit;
4014 -
4015 - echo '<div style="margin-bottom: 10px;">';
4016 - echo '<label style="display: inline-block; width: 150px;">' . esc_html($role_name) . ':</label>';
4017 - echo '<select
4018 - id="role_rate_limits_' . esc_attr($role_id) . '"
4019 - name="mxchat_options[role_rate_limits][' . esc_attr($role_id) . ']"
4020 - class="mxchat-autosave-field">';
4021 - foreach ($rate_limits as $limit) {
4022 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
4023 - }
4024 - echo '</select>';
4025 - echo '</div>';
4026 - }
4027 -
4028 - echo '<p class="description">' . esc_html__('Set the maximum number of messages users in each role can send within a 24-hour period.', 'mxchat') . '</p>';
4029 - echo '</div>';
4030 -}
4031 -
4032 -public function mxchat_rate_limit_message_callback() {
4033 - // Load the entire 'mxchat_options' array
4034 - $all_options = get_option('mxchat_options', []);
4035 -
4036 - // Retrieve the saved message or use the default value
4037 - $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
4038 - $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
4039 -
4040 - // Output the textarea
4041 - echo '<div class="pro-feature-wrapper active">';
4042 - printf(
4043 - '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
4044 - esc_textarea($rate_limit_message)
4045 - );
4046 - echo '<p class="description">' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '</p>';
4047 - echo '</div>';
4048 -}
4049 -
4050 -
4051 -private function mxchat_add_option_field($id, $title, $callback = '') {
4052 - add_settings_field(
4053 - $id,
4054 - __($title, 'mxchat'),
4055 - $callback ? array($this, $callback) : array($this, $id . '_callback'),
4056 - 'mxchat-max',
4057 - 'mxchat_setting_section_id',
4058 - $id === 'model' ? ['label_for' => 'model'] : []
4059 - );
4060 - }
4061 -
4062 -public function api_key_callback() {
4063 - // Retrieve from your stored 'api_key' in the mxchat_options array
4064 - $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
4065 -
4066 - // Notice we changed the name to "api_key" (no array notation).
4067 - echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
4068 - echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4069 -}
4070 -public function xai_api_key_callback() {
4071 - // Check if the feature is activated (paid feature)
4072 - $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
4073 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
4074 -
4075 - // Retrieve the X.AI API key value
4076 - $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
4077 -
4078 - // Render the input field for the X.AI API key
4079 - echo '<div class="' . esc_attr($class) . '">';
4080 - printf(
4081 - '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
4082 - $xaiApiKey,
4083 - $disabled
4084 - );
4085 - echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4086 -
4087 - // If the feature is not activated, show the overlay with a "Pro Only" message
4088 - if (!$this->is_activated) {
4089 - echo '<div class="pro-feature-overlay">';
4090 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
4091 - echo '</div>';
4092 - }
4093 -
4094 - echo '</div>';
4095 -}
4096 -public function claude_api_key_callback() {
4097 - // Check if the feature is activated (paid feature)
4098 - $disabled = $this->is_activated ? '' : 'disabled';
4099 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4100 -
4101 - // Retrieve the Claude API key value directly like the others
4102 - $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
4103 -
4104 - // Use direct name field like the other working API keys
4105 - echo '<div class="' . esc_attr($class) . '">';
4106 - printf(
4107 - '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
4108 - $claudeApiKey,
4109 - $disabled
4110 - );
4111 - echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4112 -
4113 - if (!$this->is_activated) {
4114 - echo '<div class="pro-feature-overlay">';
4115 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
4116 - echo '</div>';
4117 - }
4118 - echo '</div>';
4119 -}
4120 -public function deepseek_api_key_callback() {
4121 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
4122 - $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4123 -
4124 - // Notice we changed the name to "deepseek_api_key" (no array notation).
4125 - echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" />';
4126 - echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4127 -}
4128 -
4129 -
4130 -
4131 -
4132 -
4133 -public function mxchat_loops_api_key_callback() {
4134 - // Support both old and new format
4135 - $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4136 -
4137 - echo '<div class="api-key-wrapper">';
4138 - echo sprintf(
4139 - '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
4140 - $loops_api_key
4141 - );
4142 - echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4143 - echo '</div>';
4144 - echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
4145 -}
4146 -
4147 -public function mxchat_loops_mailing_list_callback() {
4148 - // Add error handling and type checking
4149 - $loops_api_key = '';
4150 - $selected_list = '';
4151 -
4152 - // Safely get the API key
4153 - if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
4154 - $loops_api_key = $this->options['loops_api_key'];
4155 - }
4156 -
4157 - // Safely get the selected list
4158 - if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
4159 - $selected_list = $this->options['loops_mailing_list'];
4160 - }
4161 -
4162 - if (!empty($loops_api_key)) {
4163 - $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4164 - if (is_array($lists) && !empty($lists)) {
4165 - echo '<select id="loops_mailing_list" name="loops_mailing_list">';
4166 - foreach ($lists as $list) {
4167 - if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4168 - echo sprintf(
4169 - '<option value="%s" %s>%s</option>',
4170 - esc_attr($list['id']),
4171 - selected($selected_list, $list['id'], false),
4172 - esc_html($list['name'])
4173 - );
4174 - }
4175 - }
4176 - echo '</select>';
4177 - } else {
4178 - echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4179 - }
4180 - } else {
4181 - echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
4182 - }
4183 -}
4184 -public function mxchat_triggered_phrase_response_callback() {
4185 - $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4186 - $triggered_response = isset($this->options['triggered_phrase_response'])
4187 - ? $this->options['triggered_phrase_response']
4188 - : $default_response;
4189 -
4190 - echo sprintf(
4191 - '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4192 - esc_textarea($triggered_response)
4193 - );
4194 - echo '<p class="description">' . esc_html__('Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.', 'mxchat') . '</p>';
4195 -}
4196 -
4197 -public function mxchat_email_capture_response_callback() {
4198 - $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4199 - $email_capture_response = isset($this->options['email_capture_response'])
4200 - ? $this->options['email_capture_response']
4201 - : $default_response;
4202 -
4203 - echo sprintf(
4204 - '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4205 - esc_textarea($email_capture_response)
4206 - );
4207 - echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
4208 -}
4209 -
4210 -public function mxchat_pre_chat_message_callback() {
4211 - // Load the entire 'mxchat_options' array
4212 - $all_options = get_option('mxchat_options', []);
4213 -
4214 - // Retrieve the saved message or use the default value
4215 - $default_message = __('Hey there! Ask me anything!', 'mxchat');
4216 - $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
4217 -
4218 - // Output the textarea
4219 - printf(
4220 - '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
4221 - esc_textarea($pre_chat_message)
4222 - );
4223 - echo '<p class="description">' . esc_html__('Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.', 'mxchat') . '</p>';
4224 -}
4225 -
4226 -
4227 -
4228 -// Callback for AI Instructions textarea
4229 -public function system_prompt_instructions_callback() {
4230 - // Retrieve the current value of the system prompt instructions
4231 - $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4232 -
4233 - // Render the textarea field
4234 - printf(
4235 - '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4236 - $instructions
4237 - );
4238 -
4239 - // Provide a helpful description
4240 - echo '<p class="description">' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '</p>';
4241 -}
4242 -
4243 -
4244 -public function mxchat_model_callback() {
4245 - // Define available models grouped by provider
4246 - $models = array(
4247 - esc_html__('X.AI Models', 'mxchat') => array(
4248 - 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4249 - 'grok-2' => esc_html__('Grok 2', 'mxchat')
4250 - ),
4251 - esc_html__('DeepSeek Models', 'mxchat') => array(
4252 - 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4253 - ),
4254 - esc_html__('Claude Models', 'mxchat') => array(
4255 - 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
4256 - 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
4257 - 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4258 - 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4259 - ),
4260 - esc_html__('OpenAI Models', 'mxchat') => array(
4261 - 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4262 - 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4263 - 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4264 - 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4265 - 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4266 - )
4267 - );
4268 -
4269 - // Retrieve the currently selected model from saved options
4270 - $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4271 -
4272 - // Begin the select dropdown
4273 - echo '<select id="model" name="model">'; // No array notation in name attribute
4274 -
4275 - // Iterate over groups of models
4276 - foreach ($models as $group_label => $group_models) {
4277 - echo '<optgroup label="' . esc_attr($group_label) . '">';
4278 -
4279 - foreach ($group_models as $model_value => $model_label) {
4280 - // Disable Pro-only models for non-activated users
4281 - $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4282 - $label_suffix = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? esc_html__(' (Pro Only)', 'mxchat') : '';
4283 -
4284 - // Output the option element
4285 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
4286 - }
4287 -
4288 - echo '</optgroup>';
4289 - }
4290 -
4291 - // Close the select dropdown
4292 - echo '</select>';
4293 -
4294 - // Add a description below the dropdown
4295 - echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
4296 -}
4297 -
4298 -public function voyage_api_key_callback() {
4299 - $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4300 - echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" />';
4301 - echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4302 -}
4303 -
4304 -// Callback function for embedding model selection
4305 -public function embedding_model_callback() {
4306 - $models = array(
4307 - esc_html__('OpenAI Embeddings', 'mxchat') => array(
4308 - 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
4309 - 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Original)', 'mxchat'),
4310 - 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
4311 - ),
4312 - esc_html__('Voyage AI Embeddings', 'mxchat') => array(
4313 - 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
4314 - )
4315 - );
4316 -
4317 - $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
4318 -
4319 - echo '<select id="embedding_model" name="embedding_model">';
4320 - foreach ($models as $group_label => $group_models) {
4321 - echo '<optgroup label="' . esc_attr($group_label) . '">';
4322 - foreach ($group_models as $model_value => $model_label) {
4323 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4324 - }
4325 - echo '</optgroup>';
4326 - }
4327 - echo '</select>';
4328 -echo '<p class="description"><span class="red-warning">IMPORTANT:</span> Select the model for vector embeddings. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
4329 -
4330 -}
4331 -
4332 -
4333 -public function mxchat_top_bar_title_callback() {
4334 - // Retrieve the current value of the top bar title from saved options
4335 - $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4336 -
4337 - // Render the input field
4338 - echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
4339 -
4340 - // Add a description
4341 - echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4342 -}
4343 -
4344 -public function enable_email_block_callback() {
4345 - // Load full plugin options array
4346 - $all_options = get_option('mxchat_options', []);
4347 -
4348 - // Get the value, default to 'off'
4349 - $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
4350 -
4351 - // Check if it's 'on'
4352 - $checked = ($enable_email_block === 'on') ? 'checked' : '';
4353 -
4354 - echo '<label class="toggle-switch">';
4355 - echo sprintf(
4356 - '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
4357 - esc_attr($checked)
4358 - );
4359 - echo '<span class="slider"></span>';
4360 - echo '</label>';
4361 - echo '<p class="description">' . esc_html__('Their email will appear at the top of the transcript. Email form will show for users who are not logged in or have not provided an email within 24h.', 'mxchat') . '</p>';
4362 -}
4363 -
4364 -
4365 -
4366 -public function email_blocker_header_content_callback() {
4367 - // Load the entire 'mxchat_options' array
4368 - $all_options = get_option('mxchat_options', []);
4369 -
4370 - // Retrieve the saved content or default to empty
4371 - $content = isset($all_options['email_blocker_header_content'])
4372 - ? $all_options['email_blocker_header_content']
4373 - : '';
4374 -
4375 - // Render the textarea
4376 - echo '<textarea
4377 - id="email_blocker_header_content"
4378 - name="email_blocker_header_content"
4379 - rows="5"
4380 - cols="70"
4381 - >' . esc_textarea($content) . '</textarea>';
4382 -
4383 - echo '<p class="description">';
4384 - echo esc_html__('You may enter HTML here, such as &lt;h2&gt;Welcome&lt;/h2&gt; or &lt;p&gt;Let\'s get started&lt;/p&gt;.', 'mxchat');
4385 - echo '</p>';
4386 -}
4387 -
4388 -
4389 -public function email_blocker_button_text_callback() {
4390 - // Load the entire 'mxchat_options' array
4391 - $all_options = get_option('mxchat_options', []);
4392 -
4393 - // Retrieve the saved button text or default to empty
4394 - $button_text = isset($all_options['email_blocker_button_text'])
4395 - ? $all_options['email_blocker_button_text']
4396 - : '';
4397 -
4398 - // Use esc_attr to safely render the existing text
4399 - echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
4400 -
4401 - echo '<p class="description">';
4402 - echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4403 - echo '</p>';
4404 -}
4405 -
4406 -
4407 -
4408 -public function mxchat_intro_message_callback() {
4409 - // Load the entire 'mxchat_options' array
4410 - $all_options = get_option('mxchat_options', []);
4411 -
4412 - // Retrieve the saved intro message or use the default
4413 - $default_message = __('Hello! How can I assist you today?', 'mxchat');
4414 - $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4415 -
4416 - // Output the textarea with the saved value
4417 - ?>
4418 - <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
4419 - <p class="description">
4420 - <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
4421 - </p>
4422 - <?php
4423 -}
4424 -
4425 -
4426 -public function mxchat_input_copy_callback() {
4427 - // Load the entire 'mxchat_options' array
4428 - $all_options = get_option('mxchat_options', []);
4429 -
4430 - // Retrieve the saved input copy or use the default value
4431 - $default_copy = __('How can I assist?', 'mxchat');
4432 - $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
4433 -
4434 - // Output the input field with the saved value
4435 - printf(
4436 - '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
4437 - esc_attr($input_copy),
4438 - esc_attr__('How can I assist?', 'mxchat')
4439 - );
4440 -
4441 - // Output the description
4442 - echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
4443 -}
4444 -
4445 -
4446 -
4447 -public function mxchat_user_message_font_color_callback() {
4448 - $disabled = $this->is_activated ? '' : 'disabled';
4449 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4450 -
4451 - echo '<div class="' . esc_attr($class) . '">';
4452 - echo sprintf(
4453 - '<input type="text"
4454 - id="user_message_font_color"
4455 - name="user_message_font_color"
4456 - value="%s"
4457 - class="my-color-field"
4458 - data-default-color="#ffffff"
4459 - %s />',
4460 - isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
4461 - esc_attr($disabled)
4462 - );
4463 -
4464 - if (!$this->is_activated) {
4465 - echo '<div class="pro-feature-overlay">';
4466 - echo '<a href="https://mxchat.ai/" target="_blank">';
4467 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4468 - echo '</a>';
4469 - echo '</div>';
4470 - }
4471 - echo '</div>';
4472 -}
4473 -
4474 -public function mxchat_bot_message_bg_color_callback() {
4475 - $disabled = $this->is_activated ? '' : 'disabled';
4476 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4477 -
4478 - echo '<div class="' . esc_attr($class) . '">';
4479 - echo sprintf(
4480 - '<input type="text"
4481 - id="bot_message_bg_color"
4482 - name="bot_message_bg_color"
4483 - value="%s"
4484 - class="my-color-field"
4485 - data-default-color="#e1e1e1"
4486 - %s />',
4487 - isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
4488 - esc_attr($disabled)
4489 - );
4490 -
4491 - if (!$this->is_activated) {
4492 - echo '<div class="pro-feature-overlay">';
4493 - echo '<a href="https://mxchat.ai/" target="_blank">';
4494 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4495 - echo '</a>';
4496 - echo '</div>';
4497 - }
4498 - echo '</div>';
4499 -}
4500 -
4501 -public function mxchat_bot_message_font_color_callback() {
4502 - $disabled = $this->is_activated ? '' : 'disabled';
4503 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4504 -
4505 - echo '<div class="' . esc_attr($class) . '">';
4506 - echo sprintf(
4507 - '<input type="text"
4508 - id="bot_message_font_color"
4509 - name="bot_message_font_color"
4510 - value="%s"
4511 - class="my-color-field"
4512 - data-default-color="#333333"
4513 - %s />',
4514 - isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
4515 - esc_attr($disabled)
4516 - );
4517 -
4518 - if (!$this->is_activated) {
4519 - echo '<div class="pro-feature-overlay">';
4520 - echo '<a href="https://mxchat.ai/" target="_blank">';
4521 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4522 - echo '</a>';
4523 - echo '</div>';
4524 - }
4525 - echo '</div>';
4526 -}
4527 -
4528 -public function mxchat_live_agent_message_bg_color_callback() {
4529 - $disabled = $this->is_activated ? '' : 'disabled';
4530 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4531 -
4532 - echo '<div class="' . esc_attr($class) . '">';
4533 - echo sprintf(
4534 - '<input type="text"
4535 - id="live_agent_message_bg_color"
4536 - name="live_agent_message_bg_color"
4537 - value="%s"
4538 - class="my-color-field"
4539 - data-default-color="#ffffff"
4540 - %s />',
4541 - isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
4542 - esc_attr($disabled)
4543 - );
4544 -
4545 - if (!$this->is_activated) {
4546 - echo '<div class="pro-feature-overlay">';
4547 - echo '<a href="https://mxchat.ai/" target="_blank">';
4548 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4549 - echo '</a>';
4550 - echo '</div>';
4551 - }
4552 - echo '</div>';
4553 -}
4554 -
4555 -public function mxchat_live_agent_message_font_color_callback() {
4556 - $disabled = $this->is_activated ? '' : 'disabled';
4557 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4558 -
4559 - echo '<div class="' . esc_attr($class) . '">';
4560 - echo sprintf(
4561 - '<input type="text"
4562 - id="live_agent_message_font_color"
4563 - name="live_agent_message_font_color"
4564 - value="%s"
4565 - class="my-color-field"
4566 - data-default-color="#333333"
4567 - %s />',
4568 - isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
4569 - esc_attr($disabled)
4570 - );
4571 -
4572 - if (!$this->is_activated) {
4573 - echo '<div class="pro-feature-overlay">';
4574 - echo '<a href="https://mxchat.ai/" target="_blank">';
4575 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4576 - echo '</a>';
4577 - echo '</div>';
4578 - }
4579 - echo '</div>';
4580 -}
4581 -
4582 -public function mxchat_mode_indicator_bg_color_callback() {
4583 - $disabled = $this->is_activated ? '' : 'disabled';
4584 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4585 -
4586 - echo '<div class="' . esc_attr($class) . '">';
4587 - echo sprintf(
4588 - '<input type="text"
4589 - id="mode_indicator_bg_color"
4590 - name="mode_indicator_bg_color"
4591 - value="%s"
4592 - class="my-color-field"
4593 - data-default-color="#767676"
4594 - %s />',
4595 - isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
4596 - esc_attr($disabled)
4597 - );
4598 -
4599 - if (!$this->is_activated) {
4600 - echo '<div class="pro-feature-overlay">';
4601 - echo '<a href="https://mxchat.ai/" target="_blank">';
4602 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4603 - echo '</a>';
4604 - echo '</div>';
4605 - }
4606 - echo '</div>';
4607 -}
4608 -
4609 -public function mxchat_mode_indicator_font_color_callback() {
4610 - $disabled = $this->is_activated ? '' : 'disabled';
4611 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4612 -
4613 - echo '<div class="' . esc_attr($class) . '">';
4614 - echo sprintf(
4615 - '<input type="text"
4616 - id="mode_indicator_font_color"
4617 - name="mode_indicator_font_color"
4618 - value="%s"
4619 - class="my-color-field"
4620 - data-default-color="#ffffff"
4621 - %s />',
4622 - isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
4623 - esc_attr($disabled)
4624 - );
4625 -
4626 - if (!$this->is_activated) {
4627 - echo '<div class="pro-feature-overlay">';
4628 - echo '<a href="https://mxchat.ai/" target="_blank">';
4629 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4630 - echo '</a>';
4631 - echo '</div>';
4632 - }
4633 - echo '</div>';
4634 -}
4635 -
4636 -public function mxchat_toolbar_icon_color_callback() {
4637 - $disabled = $this->is_activated ? '' : 'disabled';
4638 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4639 -
4640 - echo '<div class="' . esc_attr($class) . '">';
4641 - echo sprintf(
4642 - '<input type="text"
4643 - id="toolbar_icon_color"
4644 - name="toolbar_icon_color"
4645 - value="%s"
4646 - class="my-color-field"
4647 - data-default-color="#212121"
4648 - %s />',
4649 - isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
4650 - esc_attr($disabled)
4651 - );
4652 -
4653 - if (!$this->is_activated) {
4654 - echo '<div class="pro-feature-overlay">';
4655 - echo '<a href="https://mxchat.ai/" target="_blank">';
4656 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4657 - echo '</a>';
4658 - echo '</div>';
4659 - }
4660 - echo '</div>';
4661 -}
4662 -
4663 -public function mxchat_top_bar_bg_color_callback() {
4664 - $disabled = $this->is_activated ? '' : 'disabled';
4665 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4666 -
4667 - echo '<div class="' . esc_attr($class) . '">';
4668 - echo sprintf(
4669 - '<input type="text"
4670 - id="top_bar_bg_color"
4671 - name="top_bar_bg_color"
4672 - value="%s"
4673 - class="my-color-field"
4674 - data-default-color="#00b294"
4675 - %s />',
4676 - isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
4677 - esc_attr($disabled)
4678 - );
4679 -
4680 - if (!$this->is_activated) {
4681 - echo '<div class="pro-feature-overlay">';
4682 - echo '<a href="https://mxchat.ai/" target="_blank">';
4683 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4684 - echo '</a>';
4685 - echo '</div>';
4686 - }
4687 - echo '</div>';
4688 -}
4689 -
4690 -public function mxchat_send_button_font_color_callback() {
4691 - $disabled = $this->is_activated ? '' : 'disabled';
4692 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4693 -
4694 - echo '<div class="' . esc_attr($class) . '">';
4695 - echo sprintf(
4696 - '<input type="text"
4697 - id="send_button_font_color"
4698 - name="send_button_font_color"
4699 - value="%s"
4700 - class="my-color-field"
4701 - data-default-color="#ffffff"
4702 - %s />',
4703 - isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
4704 - esc_attr($disabled)
4705 - );
4706 -
4707 - if (!$this->is_activated) {
4708 - echo '<div class="pro-feature-overlay">';
4709 - echo '<a href="https://mxchat.ai/" target="_blank">';
4710 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4711 - echo '</a>';
4712 - echo '</div>';
4713 - }
4714 - echo '</div>';
4715 -}
4716 -
4717 -public function mxchat_chatbot_background_color_callback() {
4718 - $disabled = $this->is_activated ? '' : 'disabled';
4719 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4720 -
4721 - echo '<div class="' . esc_attr($class) . '">';
4722 - echo sprintf(
4723 - '<input type="text"
4724 - id="chatbot_background_color"
4725 - name="chatbot_background_color"
4726 - value="%s"
4727 - class="my-color-field"
4728 - data-default-color="#000000"
4729 - %s />',
4730 - isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
4731 - esc_attr($disabled)
4732 - );
4733 -
4734 - if (!$this->is_activated) {
4735 - echo '<div class="pro-feature-overlay">';
4736 - echo '<a href="https://mxchat.ai/" target="_blank">';
4737 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4738 - echo '</a>';
4739 - echo '</div>';
4740 - }
4741 - echo '</div>';
4742 -}
4743 -
4744 -public function mxchat_icon_color_callback() {
4745 - $disabled = $this->is_activated ? '' : 'disabled';
4746 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4747 -
4748 - echo '<div class="' . esc_attr($class) . '">';
4749 - echo sprintf(
4750 - '<input type="text"
4751 - id="icon_color"
4752 - name="icon_color"
4753 - value="%s"
4754 - class="my-color-field"
4755 - data-default-color="#ffffff"
4756 - %s />',
4757 - isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
4758 - esc_attr($disabled)
4759 - );
4760 -
4761 - if (!$this->is_activated) {
4762 - echo '<div class="pro-feature-overlay">';
4763 - echo '<a href="https://mxchat.ai/" target="_blank">';
4764 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4765 - echo '</a>';
4766 - echo '</div>';
4767 - }
4768 - echo '</div>';
4769 -}
4770 -
4771 -public function mxchat_custom_icon_callback() {
4772 - $disabled = $this->is_activated ? '' : 'disabled';
4773 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4774 - $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
4775 -
4776 - echo '<div class="' . esc_attr($class) . '">';
4777 - echo sprintf(
4778 - '<input type="url"
4779 - id="custom_icon"
4780 - name="custom_icon"
4781 - value="%s"
4782 - placeholder="%s"
4783 - class="regular-text"
4784 - %s />',
4785 - $custom_icon_url,
4786 - esc_attr__('Enter PNG URL', 'mxchat'),
4787 - esc_attr($disabled)
4788 - );
4789 -
4790 - // Preview container for the icon
4791 - if (!empty($custom_icon_url)) {
4792 - echo '<div class="icon-preview" style="margin-top: 10px;">';
4793 - echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4794 - echo '</div>';
4795 - }
4796 -
4797 - echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4798 -
4799 - if (!$this->is_activated) {
4800 - echo '<div class="pro-feature-overlay">';
4801 - echo '<a href="https://mxchat.ai/" target="_blank">';
4802 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4803 - echo '</a>';
4804 - echo '</div>';
4805 - }
4806 - echo '</div>';
4807 -}
4808 -
4809 -public function mxchat_title_icon_callback() {
4810 - $disabled = $this->is_activated ? '' : 'disabled';
4811 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4812 - // Fixed the variable reference - it was using custom_icon instead of title_icon
4813 - $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
4814 -
4815 - echo '<div class="' . esc_attr($class) . '">';
4816 - echo sprintf(
4817 - '<input type="url"
4818 - id="title_icon"
4819 - name="title_icon"
4820 - value="%s"
4821 - placeholder="%s"
4822 - class="regular-text"
4823 - %s />',
4824 - $title_icon_url,
4825 - esc_attr__('Enter PNG URL', 'mxchat'),
4826 - esc_attr($disabled)
4827 - );
4828 -
4829 - // Preview container for the icon
4830 - if (!empty($title_icon_url)) {
4831 - echo '<div class="icon-preview" style="margin-top: 10px;">';
4832 - echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4833 - echo '</div>';
4834 - }
4835 -
4836 - echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4837 -
4838 - if (!$this->is_activated) {
4839 - echo '<div class="pro-feature-overlay">';
4840 - echo '<a href="https://mxchat.ai/" target="_blank">';
4841 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4842 - echo '</a>';
4843 - echo '</div>';
4844 - }
4845 - echo '</div>';
4846 -}
4847 -
4848 -public function mxchat_chat_input_font_color_callback() {
4849 - $disabled = $this->is_activated ? '' : 'disabled';
4850 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4851 -
4852 - echo '<div class="' . esc_attr($class) . '">';
4853 - echo sprintf(
4854 - '<input type="text"
4855 - id="chat_input_font_color"
4856 - name="chat_input_font_color"
4857 - value="%s"
4858 - class="my-color-field"
4859 - data-default-color="#555555"
4860 - %s />',
4861 - isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
4862 - esc_attr($disabled)
4863 - );
4864 -
4865 - if (!$this->is_activated) {
4866 - echo '<div class="pro-feature-overlay">';
4867 - echo '<a href="https://mxchat.ai/" target="_blank">';
4868 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4869 - echo '</a>';
4870 - echo '</div>';
4871 - }
4872 - echo '</div>';
4873 -}
4874 -
4875 -public function mxchat_append_to_body_callback() {
4876 - // Get value from options array, default to 'off'
4877 - $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
4878 - $checked = ($append_to_body === 'on') ? 'checked' : '';
4879 -
4880 - echo '<label class="toggle-switch">';
4881 - echo sprintf(
4882 - '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
4883 - esc_attr($checked)
4884 - );
4885 - echo '<span class="slider"></span>';
4886 - echo '</label>';
4887 - echo '<p class="description">' .
4888 - esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
4889 - '</p>';
4890 -
4891 -}
4892 -
4893 -
4894 -
4895 -public function mxchat_privacy_toggle_callback() {
4896 - // Load from mxchat_options array
4897 - $options = get_option('mxchat_options', []);
4898 -
4899 - // Get privacy toggle value with fallback
4900 - $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
4901 - $checked = ($privacy_toggle === 'on') ? 'checked' : '';
4902 -
4903 - // Get privacy text with fallback
4904 - $privacy_text = isset($options['privacy_text'])
4905 - ? $options['privacy_text']
4906 - : __('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat');
4907 -
4908 - // Output the toggle switch
4909 - echo '<label class="toggle-switch">';
4910 - echo sprintf(
4911 - '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
4912 - esc_attr($checked)
4913 - );
4914 - echo '<span class="slider"></span>';
4915 - echo '</label>';
4916 - echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
4917 -
4918 - // Output the custom text input field
4919 - echo sprintf(
4920 - '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
4921 - esc_textarea($privacy_text)
4922 - );
4923 - echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
4924 -}
4925 -
4926 -
4927 -public function mxchat_complianz_toggle_callback() {
4928 - // Load from mxchat_options array
4929 - $options = get_option('mxchat_options', []);
4930 -
4931 - // Get complianz toggle value with fallback
4932 - $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
4933 - $checked = ($complianz_toggle === 'on') ? 'checked' : '';
4934 -
4935 - // Output the toggle switch
4936 - echo '<label class="toggle-switch">';
4937 - echo sprintf(
4938 - '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
4939 - esc_attr($checked)
4940 - );
4941 - echo '<span class="slider"></span>';
4942 - echo '</label>';
4943 -
4944 - echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
4945 -}
4946 -
4947 -public function mxchat_link_target_toggle_callback() {
4948 - // Load from mxchat_options array
4949 - $options = get_option('mxchat_options', []);
4950 -
4951 - // Get link target toggle value with fallback
4952 - $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
4953 - $checked = ($link_target_toggle === 'on') ? 'checked' : '';
4954 -
4955 - // Output the toggle switch
4956 - echo '<label class="toggle-switch">';
4957 - echo sprintf(
4958 - '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
4959 - esc_attr($checked)
4960 - );
4961 - echo '<span class="slider"></span>';
4962 - echo '</label>';
4963 - echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
4964 -}
4965 -
4966 -public function mxchat_chat_persistence_toggle_callback() {
4967 - // Load from mxchat_options array
4968 - $options = get_option('mxchat_options', []);
4969 -
4970 - // Get chat persistence toggle value with fallback
4971 - $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
4972 - $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
4973 -
4974 - // Output the toggle switch
4975 - echo '<label class="toggle-switch">';
4976 - echo sprintf(
4977 - '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
4978 - esc_attr($checked)
4979 - );
4980 - echo '<span class="slider"></span>';
4981 - echo '</label>';
4982 -
4983 - echo '<p class="description">' . esc_html__('Enable to keep chat history when users navigate tabs or return to the site within 24 hours.', 'mxchat') . '</p>';
4984 -}
4985 -
4986 -public function mxchat_popular_question_1_callback() {
4987 - // Load the full plugin options array
4988 - $all_options = get_option('mxchat_options', []);
4989 -
4990 - // Retrieve the specific option for popular_question_1
4991 - $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
4992 -
4993 - // Render the input field
4994 - printf(
4995 - '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4996 - esc_attr($popular_question_1),
4997 - esc_attr__('Enter Quick Question 1', 'mxchat')
4998 - );
4999 -
5000 - // Add a description for the field
5001 - echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
5002 -}
5003 -
5004 -
5005 -public function mxchat_popular_question_2_callback() {
5006 - // Load the full plugin options array
5007 - $all_options = get_option('mxchat_options', []);
5008 -
5009 - // Retrieve the specific option for popular_question_2
5010 - $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
5011 -
5012 - // Render the input field
5013 - printf(
5014 - '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
5015 - esc_attr($popular_question_2),
5016 - esc_attr__('Enter Quick Question 2', 'mxchat')
5017 - );
5018 -
5019 - // Add a description for the field
5020 - echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
5021 -}
5022 -
5023 -
5024 -public function mxchat_popular_question_3_callback() {
5025 - // Load the full plugin options array
5026 - $all_options = get_option('mxchat_options', []);
5027 -
5028 - // Retrieve the specific option for popular_question_3
5029 - $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
5030 -
5031 - // Render the input field
5032 - printf(
5033 - '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
5034 - esc_attr($popular_question_3),
5035 - esc_attr(__('Enter Quick Question 3', 'mxchat'))
5036 - );
5037 -
5038 - // Add a description for the field
5039 - echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
5040 -}
5041 -
5042 -public function mxchat_additional_popular_questions_callback() {
5043 - $options = get_option('mxchat_options', []);
5044 - $additional_questions = isset($options['additional_popular_questions'])
5045 - ? $options['additional_popular_questions']
5046 - : get_option('additional_popular_questions', array());
5047 -
5048 - echo '<div id="mxchat-additional-questions-container">';
5049 - if (!empty($additional_questions)) {
5050 - foreach ($additional_questions as $index => $question) {
5051 - printf(
5052 - '<div class="mxchat-question-row">
5053 - <input type="text" name="additional_popular_questions[]"
5054 - value="%s"
5055 - placeholder="%s"
5056 - class="regular-text mxchat-question-input"
5057 - data-question-index="%d" />
5058 - <button type="button" class="button mxchat-remove-question"
5059 - aria-label="%s">%s</button>
5060 - </div>',
5061 - esc_attr($question),
5062 - esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
5063 - $index,
5064 - esc_attr(__('Remove question', 'mxchat')),
5065 - esc_html__('Remove', 'mxchat')
5066 - );
5067 - }
5068 - } else {
5069 - printf(
5070 - '<div class="mxchat-question-row">
5071 - <input type="text" name="additional_popular_questions[]"
5072 - value=""
5073 - placeholder="%s"
5074 - class="regular-text mxchat-question-input"
5075 - data-question-index="0" />
5076 - <button type="button" class="button mxchat-remove-question"
5077 - aria-label="%s">%s</button>
5078 - </div>',
5079 - esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
5080 - esc_attr(__('Remove question', 'mxchat')),
5081 - esc_html__('Remove', 'mxchat')
5082 - );
5083 - }
5084 - echo '</div>';
5085 - printf(
5086 - '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5087 - esc_attr(__('Add question', 'mxchat')),
5088 - esc_html__('Add Question', 'mxchat')
5089 - );
5090 - echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5091 - echo '</div>';
5092 -}
5093 -
5094 -public function mxchat_brave_api_key_callback() {
5095 - $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
5096 -
5097 - echo '<div class="api-key-wrapper">';
5098 - echo sprintf(
5099 - '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
5100 - $brave_api_key
5101 - );
5102 - echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5103 - echo '</div>';
5104 - echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5105 -}
5106 -
5107 -public function mxchat_brave_image_count_callback() {
5108 - $brave_image_count = isset($this->options['brave_image_count'])
5109 - ? intval($this->options['brave_image_count'])
5110 - : 4;
5111 -
5112 - echo sprintf(
5113 - '<input type="number" id="brave_image_count" name="brave_image_count"
5114 - value="%d" min="1" max="6" class="small-text" />',
5115 - $brave_image_count
5116 - );
5117 - echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
5118 -}
5119 -
5120 -public function mxchat_brave_safe_search_callback() {
5121 - $brave_safe_search = isset($this->options['brave_safe_search'])
5122 - ? esc_attr($this->options['brave_safe_search'])
5123 - : 'strict';
5124 -
5125 - echo '<select id="brave_safe_search" name="brave_safe_search">';
5126 - echo sprintf(
5127 - '<option value="strict" %s>%s</option>',
5128 - selected($brave_safe_search, 'strict', false),
5129 - __('Strict', 'mxchat')
5130 - );
5131 - echo sprintf(
5132 - '<option value="off" %s>%s</option>',
5133 - selected($brave_safe_search, 'off', false),
5134 - __('Off', 'mxchat')
5135 - );
5136 - echo '</select>';
5137 - echo '<p class="description">' .
5138 - esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
5139 - '</p>';
5140 -}
5141 -
5142 -public function mxchat_brave_news_count_callback() {
5143 - $brave_news_count = isset($this->options['brave_news_count'])
5144 - ? intval($this->options['brave_news_count'])
5145 - : 3;
5146 -
5147 - echo sprintf(
5148 - '<input type="number" id="brave_news_count" name="brave_news_count"
5149 - value="%d" min="1" max="10" class="small-text" />',
5150 - $brave_news_count
5151 - );
5152 - echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
5153 -}
5154 -
5155 -public function mxchat_brave_country_callback() {
5156 - $brave_country = isset($this->options['brave_country'])
5157 - ? esc_attr($this->options['brave_country'])
5158 - : 'us';
5159 -
5160 - echo sprintf(
5161 - '<input type="text" id="brave_country" name="brave_country"
5162 - value="%s" maxlength="2" class="small-text" />',
5163 - $brave_country
5164 - );
5165 - echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
5166 -}
5167 -
5168 -public function mxchat_brave_language_callback() {
5169 - $brave_language = isset($this->options['brave_language'])
5170 - ? esc_attr($this->options['brave_language'])
5171 - : 'en';
5172 -
5173 - echo sprintf(
5174 - '<input type="text" id="brave_language" name="brave_language"
5175 - value="%s" maxlength="2" class="small-text" />',
5176 - $brave_language
5177 - );
5178 - echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
5179 -}
5180 -
5181 -
5182 -
5183 -
5184 -
5185 -// Section Callback
5186 -public function mxchat_pdf_intent_section_callback() {
5187 - echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5188 -}
5189 -
5190 -public function mxchat_chat_toolbar_toggle_callback() {
5191 - // Get chat toolbar toggle value with fallback
5192 - $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5193 - $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5194 -
5195 - // Check if the plugin is activated (paid feature)
5196 - $disabled = $this->is_activated ? '' : 'disabled';
5197 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5198 -
5199 - echo '<div class="' . esc_attr($class) . '">';
5200 -
5201 - // Output the toggle switch
5202 - echo '<label class="toggle-switch">';
5203 - echo sprintf(
5204 - '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
5205 - esc_attr($checked),
5206 - esc_attr($disabled)
5207 - );
5208 - echo '<span class="slider"></span>';
5209 - echo '</label>';
5210 -
5211 - // Pro feature overlay
5212 - if (!$this->is_activated) {
5213 - echo '<div class="pro-feature-overlay">';
5214 - echo '<a href="https://mxchat.ai/" target="_blank">';
5215 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5216 - echo '</a>';
5217 - echo '</div>';
5218 - }
5219 -
5220 - echo '</div>';
5221 - echo '<p class="description">' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '</p>';
5222 -}
5223 -
5224 -
5225 -public function mxchat_pdf_intent_trigger_text_callback() {
5226 - $disabled = $this->is_activated ? '' : 'disabled';
5227 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5228 - $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5229 -
5230 - echo '<div class="' . esc_attr($class) . '">';
5231 - echo sprintf(
5232 - '<textarea id="pdf_intent_trigger_text"
5233 - name="pdf_intent_trigger_text"
5234 - rows="3"
5235 - cols="50"
5236 - placeholder="%s"
5237 - %s>%s</textarea>',
5238 - esc_attr__('Enter trigger text', 'mxchat'),
5239 - esc_attr($disabled),
5240 - isset($this->options['pdf_intent_trigger_text'])
5241 - ? esc_textarea($this->options['pdf_intent_trigger_text'])
5242 - : esc_textarea($default_text)
5243 - );
5244 - echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5245 -
5246 - if (!$this->is_activated) {
5247 - echo '<div class="pro-feature-overlay">';
5248 - echo '<a href="https://mxchat.ai/" target="_blank">';
5249 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5250 - echo '</a>';
5251 - echo '</div>';
5252 - }
5253 - echo '</div>';
5254 -}
5255 -
5256 -public function mxchat_pdf_intent_success_text_callback() {
5257 - $disabled = $this->is_activated ? '' : 'disabled';
5258 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5259 - $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5260 -
5261 - echo '<div class="' . esc_attr($class) . '">';
5262 - echo sprintf(
5263 - '<textarea id="pdf_intent_success_text"
5264 - name="pdf_intent_success_text"
5265 - rows="3"
5266 - cols="50"
5267 - placeholder="%s"
5268 - %s>%s</textarea>',
5269 - esc_attr__('Enter success text', 'mxchat'),
5270 - esc_attr($disabled),
5271 - isset($this->options['pdf_intent_success_text'])
5272 - ? esc_textarea($this->options['pdf_intent_success_text'])
5273 - : esc_textarea($default_text)
5274 - );
5275 - echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5276 -
5277 - if (!$this->is_activated) {
5278 - echo '<div class="pro-feature-overlay">';
5279 - echo '<a href="https://mxchat.ai/" target="_blank">';
5280 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5281 - echo '</a>';
5282 - echo '</div>';
5283 - }
5284 - echo '</div>';
5285 -}
5286 -
5287 -public function mxchat_pdf_intent_error_text_callback() {
5288 - $disabled = $this->is_activated ? '' : 'disabled';
5289 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5290 - $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5291 -
5292 - echo '<div class="' . esc_attr($class) . '">';
5293 - echo sprintf(
5294 - '<textarea id="pdf_intent_error_text"
5295 - name="pdf_intent_error_text"
5296 - rows="3"
5297 - cols="50"
5298 - placeholder="%s"
5299 - %s>%s</textarea>',
5300 - esc_attr__('Enter error text', 'mxchat'),
5301 - esc_attr($disabled),
5302 - isset($this->options['pdf_intent_error_text'])
5303 - ? esc_textarea($this->options['pdf_intent_error_text'])
5304 - : esc_textarea($default_text)
5305 - );
5306 - echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5307 -
5308 - if (!$this->is_activated) {
5309 - echo '<div class="pro-feature-overlay">';
5310 - echo '<a href="https://mxchat.ai/" target="_blank">';
5311 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5312 - echo '</a>';
5313 - echo '</div>';
5314 - }
5315 - echo '</div>';
5316 -}
5317 -
5318 -public function mxchat_pdf_max_pages_callback() {
5319 - $disabled = $this->is_activated ? '' : 'disabled';
5320 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5321 - $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5322 -
5323 - echo '<div class="' . esc_attr($class) . '">';
5324 - echo sprintf(
5325 - '<input type="range"
5326 - id="pdf_max_pages"
5327 - name="pdf_max_pages"
5328 - min="1"
5329 - max="69"
5330 - value="%d"
5331 - %s
5332 - class="range-slider" />',
5333 - esc_attr($max_pages),
5334 - esc_attr($disabled)
5335 - );
5336 - echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5337 - echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5338 -
5339 - if (!$this->is_activated) {
5340 - echo '<div class="pro-feature-overlay">';
5341 - echo '<a href="https://mxchat.ai/" target="_blank">';
5342 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5343 - echo '</a>';
5344 - echo '</div>';
5345 - }
5346 - echo '</div>';
5347 -}
5348 -
5349 -public function mxchat_live_agent_status_callback() {
5350 - $disabled = $this->is_activated ? '' : 'disabled';
5351 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5352 - $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off';
5353 -
5354 - echo '<div class="' . esc_attr($class) . '">';
5355 - echo '<label class="toggle-switch">';
5356 - echo sprintf(
5357 - '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s %s />',
5358 - checked($status, 'on', false),
5359 - esc_attr($disabled)
5360 - );
5361 - echo '<span class="slider"></span>';
5362 - echo '</label>';
5363 - echo '<label for="live_agent_status" class="mxchat-status-label">';
5364 - echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5365 - echo '</label>';
5366 -
5367 - if (!$this->is_activated) {
5368 - echo '<div class="pro-feature-overlay">';
5369 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5370 - echo '</div>';
5371 - }
5372 - echo '</div>';
5373 -}
5374 -
5375 -
5376 -// Away Message Callback
5377 -public function mxchat_live_agent_away_message_callback() {
5378 - $disabled = $this->is_activated ? '' : 'disabled';
5379 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5380 - $message = isset($this->options['live_agent_away_message'])
5381 - ? $this->options['live_agent_away_message']
5382 - : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5383 -
5384 - echo '<div class="' . esc_attr($class) . '">';
5385 - printf(
5386 - '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50" %s>%s</textarea>',
5387 - esc_attr($disabled),
5388 - esc_textarea($message)
5389 - );
5390 - echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5391 -
5392 - if (!$this->is_activated) {
5393 - echo '<div class="pro-feature-overlay">';
5394 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5395 - echo '</div>';
5396 - }
5397 - echo '</div>';
5398 -}
5399 -
5400 -public function mxchat_live_agent_notification_message_callback() {
5401 - $disabled = $this->is_activated ? '' : 'disabled';
5402 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5403 - $message = isset($this->options['live_agent_notification_message'])
5404 - ? $this->options['live_agent_notification_message']
5405 - : __('Live agent has been notified.', 'mxchat');
5406 -
5407 - echo '<div class="' . esc_attr($class) . '">';
5408 - printf(
5409 - '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50" %s>%s</textarea>',
5410 - esc_attr($disabled),
5411 - esc_textarea($message)
5412 - );
5413 - echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5414 -
5415 - if (!$this->is_activated) {
5416 - echo '<div class="pro-feature-overlay">';
5417 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5418 - echo '</div>';
5419 - }
5420 - echo '</div>';
5421 -}
5422 -
5423 -public function mxchat_live_agent_webhook_url_callback() {
5424 - $disabled = $this->is_activated ? '' : 'disabled';
5425 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5426 - $webhook_url = isset($this->options['live_agent_webhook_url'])
5427 - ? esc_url($this->options['live_agent_webhook_url'])
5428 - : esc_url(get_option('live_agent_webhook_url', ''));
5429 -
5430 - echo '<div class="' . esc_attr($class) . '">';
5431 - printf(
5432 - '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" %s />',
5433 - $webhook_url,
5434 - esc_attr($disabled)
5435 - );
5436 - echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5437 - echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
5438 -
5439 - if (!$this->is_activated) {
5440 - echo '<div class="pro-feature-overlay">';
5441 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5442 - echo '</div>';
5443 - }
5444 - echo '</div>';
5445 -}
5446 -
5447 -
5448 -public function mxchat_similarity_threshold_callback() {
5449 - // Load from mxchat_options array
5450 - $options = get_option('mxchat_options', []);
5451 -
5452 - // Get value with backwards compatibility
5453 - $threshold = isset($options['similarity_threshold'])
5454 - ? $options['similarity_threshold']
5455 - : get_option('mxchat_similarity_threshold', 80);
5456 -
5457 - echo '<div class="slider-container">';
5458 - echo sprintf(
5459 - '<input type="range"
5460 - id="similarity_threshold"
5461 - name="similarity_threshold"
5462 - min="70"
5463 - max="85"
5464 - step="1"
5465 - value="%s"
5466 - class="range-slider" />',
5467 - esc_attr($threshold)
5468 - );
5469 - echo sprintf(
5470 - '<span id="threshold_value" class="range-value">%s</span>',
5471 - esc_html($threshold)
5472 - );
5473 - echo '</div>';
5474 -
5475 - echo '<p class="description">';
5476 - echo esc_html__('Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge.', 'mxchat');
5477 - echo '</p>';
5478 -}
5479 -
5480 -
5481 -
5482 -public function mxchat_live_agent_secret_key_callback() {
5483 - $disabled = $this->is_activated ? '' : 'disabled';
5484 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5485 -
5486 - echo '<div class="' . esc_attr($class) . '">';
5487 - printf(
5488 - '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" %s />',
5489 - isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
5490 - esc_attr($disabled)
5491 - );
5492 - echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5493 - echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5494 -
5495 - if (!$this->is_activated) {
5496 - echo '<div class="pro-feature-overlay">';
5497 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5498 - echo '</div>';
5499 - }
5500 - echo '</div>';
5501 -}
5502 -
5503 -public function mxchat_live_agent_bot_token_callback() {
5504 - $disabled = $this->is_activated ? '' : 'disabled';
5505 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5506 -
5507 - echo '<div class="' . esc_attr($class) . '">';
5508 - printf(
5509 - '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" %s />',
5510 - isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
5511 - esc_attr($disabled)
5512 - );
5513 - echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5514 - echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5515 -
5516 - if (!$this->is_activated) {
5517 - echo '<div class="pro-feature-overlay">';
5518 - echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="' . esc_attr__('Pro Only', 'mxchat') . '" /></a>';
5519 - echo '</div>';
5520 - }
5521 - echo '</div>';
5522 -}
5523 -
5524 -public function mxchat_enqueue_admin_assets() {
5525 - wp_enqueue_style('wp-color-picker');
5526 -
5527 - // Get the plugin version or file modification time for cache busting
5528 - $plugin_version = '2.0.6'; // Replace this with your plugin's version
5529 -
5530 - // File paths
5531 - $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5532 - $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5533 - $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5534 - $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5535 - $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5536 - $transcripts_css_path = plugin_dir_path(__FILE__) . '../css/chat-transcripts.css';
5537 - $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
5538 -
5539 -
5540 - // Check if files exist and get modification times
5541 - $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5542 - $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5543 - $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5544 - $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5545 - $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5546 - $transcripts_css_version = file_exists($transcripts_css_path) ? filemtime($transcripts_css_path) : $plugin_version;
5547 - $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5548 -
5549 -
5550 - // Enqueue scripts and styles with corrected paths
5551 - wp_enqueue_script(
5552 - 'mxchat-color-picker',
5553 - plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
5554 - array('wp-color-picker'),
5555 - $color_picker_version,
5556 - true
5557 - );
5558 -
5559 - wp_enqueue_script(
5560 - 'mxchat-embedding-check',
5561 - plugin_dir_url(__FILE__) . '../js/embedding-check.js',
5562 - array(),
5563 - $embedding_check_version,
5564 - true
5565 - );
5566 -
5567 - wp_enqueue_script(
5568 - 'mxchat-admin-js',
5569 - plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5570 - array('jquery'),
5571 - $plugin_version,
5572 - true
5573 - );
5574 -
5575 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
5576 - 'ajax_url' => admin_url('admin-ajax.php'),
5577 - 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5578 - 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5579 - 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5580 - 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
5581 - ));
5582 -
5583 - // Enqueue the admin CSS
5584 - wp_enqueue_style(
5585 - 'mxchat-admin-css',
5586 - plugin_dir_url(__FILE__) . '../css/admin-style.css',
5587 - array(),
5588 - $admin_css_version
5589 - );
5590 -
5591 - if (isset($_GET['page']) && $_GET['page'] === 'mxchat-transcripts') {
5592 - wp_enqueue_style(
5593 - 'mxchat-chat-transcripts-css',
5594 - plugin_dir_url(__FILE__) . '../css/chat-transcripts.css',
5595 - array(),
5596 - $transcripts_css_version
5597 - );
5598 -
5599 - wp_enqueue_script(
5600 - 'mxchat-transcripts-js',
5601 - plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5602 - array('jquery'),
5603 - $transcripts_js_version,
5604 - true
5605 - );
5606 - }
5607 -
5608 - wp_enqueue_style(
5609 - 'mxchat-knowledge-css',
5610 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5611 - array(),
5612 - $knowledge_css_version
5613 - );
5614 -
5615 -
5616 - wp_enqueue_style(
5617 - 'mxchat-intent-css',
5618 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5619 - array(),
5620 - $intent_css_version
5621 - );
5622 -
5623 - // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5624 - wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5625 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
5626 - 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5627 - ) );
5628 -
5629 - // Localize the script for color picker and settings
5630 - wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5631 - 'ajax_url' => admin_url('admin-ajax.php'),
5632 - 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5633 - 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5634 - 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5635 - 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5636 - 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5637 - 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5638 - 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5639 - 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5640 - 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5641 - 'icon_color' => $this->options['icon_color'] ?? '#fff',
5642 - 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5643 - 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5644 - 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5645 - 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5646 - 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5647 - 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5648 - 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5649 - 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
5650 - 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
5651 - 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
5652 - 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5653 - 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5654 - 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5655 - 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5656 - 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5657 - 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5658 - 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5659 - 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5660 - 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5661 - 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5662 - ));
5663 -}
5664 -
5665 -
5666 -public function mxchat_sanitize($input) {
5667 - $new_input = array();
5668 -
5669 - if (isset($input['api_key'])) {
5670 - $new_input['api_key'] = sanitize_text_field($input['api_key']);
5671 - }
5672 -
5673 - if (isset($input['similarity_threshold'])) {
5674 - $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5675 - $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
5676 - }
5677 -
5678 - if (isset($input['xai_api_key'])) {
5679 - $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
5680 - }
5681 -
5682 - if (isset($input['claude_api_key'])) {
5683 - $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5684 - }
5685 -
5686 - if (isset($input['deepseek_api_key'])) {
5687 - $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5688 - }
5689 -
5690 - if (isset($input['enable_woocommerce_integration'])) {
5691 - $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5692 - }
5693 -
5694 - if (isset($input['privacy_toggle'])) {
5695 - $new_input['privacy_toggle'] = $input['privacy_toggle'];
5696 - }
5697 -
5698 - if (isset($input['complianz_toggle'])) {
5699 - $new_input['complianz_toggle'] = $input['complianz_toggle'];
5700 - }
5701 -
5702 - // Handle custom privacy text input
5703 - if (isset($input['privacy_text'])) {
5704 - // Allow basic HTML for links
5705 - $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
5706 - }
5707 -
5708 - if (isset($input['system_prompt_instructions'])) {
5709 - $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
5710 - }
5711 -
5712 - if (isset($input['mxchat_pro_email'])) {
5713 - $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
5714 - }
5715 -
5716 - if (isset($input['mxchat_activation_key'])) {
5717 - $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
5718 - }
5719 -
5720 - if (isset($input['append_to_body'])) {
5721 - $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5722 - }
5723 -
5724 - if (isset($input['top_bar_title'])) {
5725 - $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5726 - }
5727 -
5728 - if (isset($input['enable_email_block'])) {
5729 - $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5730 - }
5731 -
5732 - if (isset($input['email_blocker_header_content'])) {
5733 - // wp_kses_post() allows standard HTML tags permitted by WordPress
5734 - $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
5735 - }
5736 - if (isset($input['email_blocker_button_text'])) {
5737 - $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5738 - }
5739 -
5740 - if (isset($input['intro_message'])) {
5741 - $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
5742 - }
5743 -
5744 - if (isset($input['input_copy'])) {
5745 - $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
5746 - }
5747 -
5748 - if (isset($input['rate_limit_message'])) {
5749 - $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5750 - }
5751 -
5752 - if (isset($input['rate_limit_logged_out'])) {
5753 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5754 - $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
5755 -
5756 - if (in_array($rate_limit, $allowed_limits, true)) {
5757 - $new_input['rate_limit_logged_out'] = $rate_limit;
5758 - } else {
5759 - $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
5760 - }
5761 - }
5762 -
5763 - // Handle role rate limits
5764 - if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) {
5765 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5766 - $new_input['role_rate_limits'] = array();
5767 -
5768 - foreach ($input['role_rate_limits'] as $role_id => $rate_limit) {
5769 - $rate_limit = sanitize_text_field($rate_limit);
5770 - if (in_array($rate_limit, $allowed_limits, true)) {
5771 - $new_input['role_rate_limits'][$role_id] = $rate_limit;
5772 - } else {
5773 - $new_input['role_rate_limits'][$role_id] = '100'; // Default
5774 - }
5775 - }
5776 - }
5777 -
5778 - if (isset($input['pre_chat_message'])) {
5779 - $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5780 - }
5781 -
5782 - if (isset($input['voyage_api_key'])) {
5783 - $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
5784 - }
5785 -
5786 - // Add to your sanitize function
5787 - if (isset($input['embedding_model'])) {
5788 - $allowed_models = array(
5789 - 'text-embedding-ada-002',
5790 - 'text-embedding-3-small',
5791 - 'text-embedding-3-large',
5792 - 'voyage-3-large'
5793 - );
5794 - if (in_array($input['embedding_model'], $allowed_models)) {
5795 - $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5796 - }
5797 - }
5798 -
5799 - if (isset($input['model'])) {
5800 - $allowed_models = array(
5801 - 'grok-beta',
5802 - 'grok-2',
5803 - 'deepseek-chat',
5804 - 'claude-3-5-sonnet-20241022',
5805 - 'claude-3-opus-20240229',
5806 - 'claude-3-sonnet-20240229',
5807 - 'claude-3-haiku-20240307',
5808 - 'gpt-4o',
5809 - 'gpt-4o-mini',
5810 - 'gpt-4-turbo',
5811 - 'gpt-4',
5812 - 'gpt-3.5-turbo',
5813 - );
5814 - if (in_array($input['model'], $allowed_models)) {
5815 - $new_input['model'] = sanitize_text_field($input['model']);
5816 - }
5817 - }
5818 -
5819 - if (isset($input['close_button_color'])) {
5820 - $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5821 - }
5822 -
5823 - if (isset($input['chatbot_bg_color'])) {
5824 - $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
5825 - }
5826 -
5827 - if (isset($input['woocommerce_consumer_key'])) {
5828 - $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
5829 - }
5830 -
5831 - if (isset($input['woocommerce_consumer_secret'])) {
5832 - $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
5833 - }
5834 -
5835 - if (isset($input['user_message_bg_color'])) {
5836 - $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
5837 - }
5838 -
5839 - if (isset($input['user_message_font_color'])) {
5840 - $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
5841 - }
5842 -
5843 - if (isset($input['bot_message_bg_color'])) {
5844 - $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
5845 - }
5846 -
5847 - if (isset($input['bot_message_font_color'])) {
5848 - $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
5849 - }
5850 -
5851 - if (isset($input['live_agent_message_bg_color'])) {
5852 - $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
5853 - }
5854 -
5855 - if (isset($input['live_agent_message_font_color'])) {
5856 - $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
5857 - }
5858 -
5859 - if (isset($input['mode_indicator_bg_color'])) {
5860 - $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
5861 - }
5862 -
5863 - if (isset($input['mode_indicator_font_color'])) {
5864 - $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
5865 - }
5866 -
5867 - if (isset($input['toolbar_icon_color'])) {
5868 - $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
5869 - }
5870 -
5871 - if (isset($input['top_bar_bg_color'])) {
5872 - $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5873 - }
5874 -
5875 - if (isset($input['send_button_font_color'])) {
5876 - $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5877 - }
5878 -
5879 - if (isset($input['chatbot_background_color'])) {
5880 - $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
5881 - }
5882 -
5883 - if (isset($input['icon_color'])) {
5884 - $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
5885 - }
5886 -
5887 - if (isset($input['custom_icon'])) {
5888 - $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
5889 - }
5890 -
5891 - if (isset($input['title_icon'])) {
5892 - $new_input['title_icon'] = esc_url_raw($input['title_icon']);
5893 - }
5894 -
5895 - if (isset($input['chat_input_font_color'])) {
5896 - $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
5897 - }
5898 -
5899 - // Sanitize link_target_toggle
5900 - if (isset($input['link_target_toggle'])) {
5901 - $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
5902 - }
5903 -
5904 - // Sanitize Loops API Key
5905 - if (isset($input['loops_api_key'])) {
5906 - $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
5907 - }
5908 -
5909 - if (isset($input['chat_persistence_toggle'])) {
5910 - $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
5911 - }
5912 -
5913 - if (isset($input['popular_question_1'])) {
5914 - $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
5915 - }
5916 -
5917 - if (isset($input['popular_question_2'])) {
5918 - $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
5919 - }
5920 -
5921 - if (isset($input['popular_question_3'])) {
5922 - $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
5923 - }
5924 -
5925 - if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
5926 - $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
5927 - }
5928 -
5929 - // Sanitize Loops Mailing List
5930 - if (isset($input['loops_mailing_list'])) {
5931 - $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5932 - }
5933 -
5934 - // Sanitize Triggered Phrase Response
5935 - if (isset($input['triggered_phrase_response'])) {
5936 - $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5937 - }
5938 -
5939 - if (isset($input['email_capture_response'])) {
5940 - $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
5941 - }
5942 -
5943 - // Sanitize Brave Search Settings
5944 - if (isset($input['brave_api_key'])) {
5945 - $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
5946 - }
5947 -
5948 - if (isset($input['brave_image_count'])) {
5949 - $image_count = intval($input['brave_image_count']);
5950 - $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
5951 - }
5952 -
5953 - if (isset($input['brave_safe_search'])) {
5954 - $allowed = array('strict', 'off');
5955 - $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
5956 - }
5957 -
5958 - if (isset($input['brave_news_count'])) {
5959 - $news_count = intval($input['brave_news_count']);
5960 - $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
5961 - }
5962 -
5963 - if (isset($input['brave_country'])) {
5964 - $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
5965 - }
5966 -
5967 - if (isset($input['brave_language'])) {
5968 - $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
5969 - }
5970 -
5971 - if (isset($input['chat_toolbar_toggle'])) {
5972 - $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5973 - }
5974 -
5975 - if (isset($input['pdf_intent_trigger_text'])) {
5976 - $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5977 - }
5978 -
5979 - if (isset($input['pdf_intent_success_text'])) {
5980 - $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
5981 - }
5982 -
5983 - if (isset($input['pdf_intent_error_text'])) {
5984 - $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
5985 - }
5986 -
5987 - if (isset($input['pdf_max_pages'])) {
5988 - $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
5989 - if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
5990 - $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
5991 - }
5992 - }
5993 -
5994 - if (isset($input['live_agent_webhook_url'])) {
5995 - $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
5996 - }
5997 - if (isset($input['live_agent_secret_key'])) {
5998 - $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
5999 - }
6000 -
6001 - // Live Agent Integration
6002 - if (isset($input['live_agent_bot_token'])) {
6003 - $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
6004 - }
6005 -
6006 - if (isset($input['live_agent_status'])) {
6007 - $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
6008 - }
6009 - if (isset($input['live_agent_away_message'])) {
6010 - $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
6011 - }
6012 - if (isset($input['live_agent_notification_message'])) {
6013 - $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
6014 - }
6015 -
6016 - return $new_input;
6017 -}
6018 -
6019 -
6020 - // Method to append the chatbot to the body
6021 - public function mxchat_append_chatbot_to_body() {
6022 - $options = get_option('mxchat_options');
6023 - if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
6024 - echo do_shortcode('[mxchat_chatbot floating="yes"]');
6025 - }
6026 - }
6027 -
6028 -
6029 -
6030 -private static function mxchat_extract_main_content($html) {
6031 - if (empty($html)) {
6032 - //error_log('mxchat_extract_main_content: Empty HTML content received');
6033 - return '';
6034 - }
6035 -
6036 - try {
6037 - $dom = new DOMDocument;
6038 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
6039 -
6040 - // Simple load of HTML, with @ to suppress warnings
6041 - @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
6042 -
6043 - $xpath = new DOMXPath($dom);
6044 -
6045 - // Simplified selectors focusing on common content areas
6046 - $selectors = [
6047 - '//article',
6048 - '//*[@id="content"]',
6049 - '//*[@class="entry-content"]',
6050 - '//main'
6051 - ];
6052 -
6053 - foreach ($selectors as $selector) {
6054 - $nodes = $xpath->query($selector);
6055 - if ($nodes && $nodes->length > 0) {
6056 - $content = '';
6057 - foreach ($nodes as $node) {
6058 - $content .= $dom->saveHTML($node);
6059 - }
6060 - if (!empty($content)) {
6061 - return $content;
6062 - }
6063 - }
6064 - }
6065 -
6066 - // Fallback: Return the entire body content if no specific selector matches
6067 - $body = $dom->getElementsByTagName('body');
6068 - if ($body->length > 0) {
6069 - return $dom->saveHTML($body->item(0));
6070 - }
6071 -
6072 - // Last resort: return the original HTML
6073 - return $html;
6074 -
6075 - } catch (Exception $e) {
6076 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
6077 - return $html; // Return original HTML if parsing fails
6078 - } finally {
6079 - libxml_clear_errors();
6080 - }
6081 -}
6082 -
6083 -
6084 -private function mxchat_fetch_loops_mailing_lists($api_key) {
6085 - $url = 'https://app.loops.so/api/v1/lists';
6086 - $response = wp_remote_get($url, array(
6087 - 'headers' => array(
6088 - 'Authorization' => 'Bearer ' . $api_key,
6089 - 'Content-Type' => 'application/json'
6090 - )
6091 - ));
6092 -
6093 - if (is_wp_error($response)) {
6094 - return array();
6095 - }
6096 -
6097 - $body = wp_remote_retrieve_body($response);
6098 - $lists = json_decode($body, true);
6099 -
6100 - return isset($lists) && is_array($lists) ? $lists : array();
6101 -}
6102 -
6103 -
6104 -
6105 -
6106 -
6107 -function mxchat_calculate_cosine_similarity($vec1, $vec2) {
6108 - if (empty($vec1) || empty($vec2)) {
6109 - return 0.0;
6110 - }
6111 -
6112 - $dot_product = 0.0;
6113 - $norm_a = 0.0;
6114 - $norm_b = 0.0;
6115 -
6116 - for ($i = 0; $i < count($vec1); $i++) {
6117 - $dot_product += $vec1[$i] * $vec2[$i];
6118 - $norm_a += pow($vec1[$i], 2);
6119 - $norm_b += pow($vec2[$i], 2);
6120 - }
6121 -
6122 - if ($norm_a == 0.0 || $norm_b == 0.0) {
6123 - return 0.0;
6124 - } else {
6125 - return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
6126 - }
6127 -}
6128 -
6129 -
6130 -
6131 -
6132 -}
6133 -?>
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_init', array($this, 'mxchat_prompts_page_init')); // Add this line
25 + add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 + add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
27 + add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
28 + add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 + add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 + add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 + add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
32 + add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 + add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 + add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 + add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
36 + add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
37 + // Add the AJAX handler for logged-in users
38 + add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
39 + add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
40 +
41 + // Define admin_post actions for form submission handling
42 + add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
43 + add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
44 + add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
45 + add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
46 + add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
47 +
48 + add_action('save_post', array($this, 'handle_post_update'), 10, 3);
49 + add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
50 +add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
51 +
52 +
53 + }
54 +
55 + // Method to check if the license is active
56 + private function is_license_active() {
57 + $license_status = get_option('mxchat_license_status', 'inactive');
58 + return $license_status === 'active';
59 + }
60 +
61 + // Initialize default options
62 +private function initialize_default_options() {
63 + $default_options = array(
64 + 'api_key' => '',
65 + 'xai_api_key' => '',
66 + 'claude_api_key' => '',
67 + '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:
68 + - Your name is [Chatbot Name].
69 + - 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.
70 + - Do not make up links to pages. Only use specific links you see in your knowledgebase.
71 + - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
72 + - 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.',
73 + 'model' => 'gpt-4o',
74 + 'rate_limit_logged_in' => '100',
75 + 'rate_limit_logged_out' => '100',
76 + 'rate_limit_message' => 'Rate limit exceeded. Please try again later.',
77 + 'enable_email_block' => '',
78 + 'email_blocker_header_content' => "<h2>Welcome to Our Chat!</h2>\n<p>Let's get started. Enter your email to begin chatting with us.</p>",
79 + 'email_blocker_button_text' => 'Start Chat',
80 + 'top_bar_title' => 'MxChat',
81 + 'intro_message' => 'Hello! How can I assist you today?',
82 + 'input_copy' => 'How can I assist?',
83 + 'append_to_body' => 'off',
84 + 'close_button_color' => '#fff',
85 + 'chatbot_bg_color' => '#fff',
86 + 'user_message_bg_color' => '#fff',
87 + 'user_message_font_color' => '#212121',
88 + 'bot_message_bg_color' => '#212121',
89 + 'bot_message_font_color' => '#fff',
90 + 'top_bar_bg_color' => '#212121',
91 + 'send_button_font_color' => '#212121',
92 + 'chat_input_font_color' => '#212121',
93 + 'chatbot_background_color' => '#212121',
94 + 'icon_color' => '#fff',
95 + 'enable_woocommerce_integration' => '0',
96 + 'link_target_toggle' => 'off',
97 + 'pre_chat_message' => 'Hey there! Ask me anything!',
98 +
99 + // New fields for Loops Integration
100 + 'loops_api_key' => '',
101 + 'loops_mailing_list' => '',
102 + 'triggered_phrase_response' => 'Would you like to join our mailing list? Please provide your email below.',
103 + 'email_capture_response' => 'Thank you for providing your email! You\'ve been added to our list.',
104 + 'popular_question_1' => '',
105 + 'popular_question_2' => '',
106 + 'popular_question_3' => '',
107 + 'pdf_intent_trigger_text' => "Please provide the URL to the PDF you'd like to discuss.",
108 + 'pdf_intent_success_text' => "I've processed the PDF. What questions do you have about it?",
109 + 'pdf_intent_error_text' => "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
110 + 'pdf_max_pages' => 69,
111 +
112 + // Live Agent Integration
113 + 'live_agent_webhook_url' => '',
114 + 'live_agent_secret_key' => '',
115 + 'live_agent_bot_token' => '',
116 + 'live_agent_message_bg_color' => '#ffffff',
117 + 'live_agent_message_font_color' => '#333333',
118 + 'chat_toolbar_toggle' => 'off',
119 + 'mode_indicator_bg_color' => '#767676',
120 + 'mode_indicator_font_color' => '#ffffff',
121 + 'toolbar_icon_color' => '#212121',
122 + );
123 +
124 +
125 + // Merge existing options with defaults
126 + $existing_options = get_option('mxchat_options', array());
127 + $merged_options = wp_parse_args($existing_options, $default_options);
128 +
129 + // Update the options if they have changed
130 + if ($existing_options !== $merged_options) {
131 + update_option('mxchat_options', $merged_options);
132 + }
133 +
134 + // Update the $this->options property
135 + $this->options = $merged_options;
136 + }
137 +
138 +
139 +
140 + public function mxchat_add_plugin_page() {
141 + // Main menu page
142 + add_menu_page(
143 + 'MxChat Settings',
144 + 'MxChat',
145 + 'manage_options',
146 + 'mxchat-max',
147 + array($this, 'mxchat_create_admin_page'),
148 + 'dashicons-testimonial',
149 + 6
150 + );
151 +
152 + // Submenu page for Knowledge
153 + add_submenu_page(
154 + 'mxchat-max',
155 + 'Prompts',
156 + 'Knowledge',
157 + 'manage_options',
158 + 'mxchat-prompts',
159 + array($this, 'mxchat_create_prompts_page')
160 + );
161 +
162 + // Submenu page for Chat Transcripts
163 + add_submenu_page(
164 + 'mxchat-max', // Corrected parent slug to match the main menu
165 + 'Chat Transcripts',
166 + 'Transcripts',
167 + 'manage_options',
168 + 'mxchat-transcripts',
169 + array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
170 + );
171 +
172 + // Submenu page for Intents
173 + add_submenu_page(
174 + 'mxchat-max', // Parent slug
175 + 'MxChat Intents', // Page title
176 + 'Intents', // Menu title
177 + 'manage_options', // Capability
178 + 'mxchat-intents', // Menu slug
179 + array($this, 'mxchat_intents_page_html') // Callback function
180 + );
181 +
182 + // Submenu page for Activation Key
183 + add_submenu_page(
184 + 'mxchat-max',
185 + 'Pro Upgrade',
186 + 'Pro Upgrade',
187 + 'manage_options',
188 + 'mxchat-activation',
189 + array($this, 'mxchat_create_activation_page')
190 + );
191 +
192 +
193 +}
194 +
195 +public function mxchat_save_setting_callback() {
196 + check_ajax_referer('mxchat_save_setting_nonce');
197 +
198 + if (!current_user_can('manage_options')) {
199 + wp_send_json_error(['message' => 'Unauthorized']);
200 + }
201 +
202 + $name = isset($_POST['name']) ? $_POST['name'] : '';
203 + // Strip slashes from the value before saving
204 + $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
205 +
206 + if (empty($name)) {
207 + wp_send_json_error(['message' => 'Invalid field name']);
208 + }
209 +
210 + // Load the full options array
211 + $options = get_option('mxchat_options', []);
212 +
213 + // Handle special cases
214 + switch ($name) {
215 + case 'additional_popular_questions':
216 + $questions = json_decode($value, true); // No need for stripslashes here
217 + if (is_array($questions)) {
218 + $options[$name] = $questions;
219 + // Also update old option for backwards compatibility
220 + update_option('additional_popular_questions', $questions);
221 + }
222 + break;
223 +
224 + case 'close_button_color':
225 + case 'user_message_bg_color':
226 + case 'user_message_font_color':
227 + case 'bot_message_bg_color':
228 + case 'bot_message_font_color':
229 + case 'top_bar_bg_color':
230 + case 'send_button_font_color':
231 + case 'chatbot_background_color':
232 + case 'icon_color':
233 + case 'chat_input_font_color':
234 + case 'live_agent_message_bg_color':
235 + case 'live_agent_message_font_color':
236 + case 'mode_indicator_bg_color':
237 + case 'mode_indicator_font_color':
238 + case 'toolbar_icon_color':
239 + // Store color values directly
240 + $options[$name] = $value;
241 + break;
242 +
243 + case 'enable_woocommerce_integration':
244 + // Handle values that used to be 1/0
245 + $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
246 + break;
247 +
248 + default:
249 + // Handle toggles
250 + if (strpos($name, 'toggle') !== false || in_array($name, ['chat_persistence_toggle', 'privacy_toggle', 'complianz_toggle', 'chat_toolbar_toggle'])) {
251 + $options[$name] = ($value === 'on') ? 'on' : 'off';
252 + } else {
253 + // Store all other values directly
254 + $options[$name] = $value;
255 + }
256 + break;
257 + }
258 +
259 + // Save all updates
260 + $updated = update_option('mxchat_options', $options);
261 +
262 + // Handle backwards compatibility for certain fields
263 + $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
264 + 'brave_country', 'brave_language', 'similarity_threshold'];
265 +
266 + if (in_array($name, $legacy_fields)) {
267 + // Also update the individual option for backwards compatibility
268 + update_option($name, $value);
269 + }
270 +
271 + if ($updated) {
272 + wp_send_json_success(['message' => 'Setting saved']);
273 + } else {
274 + wp_send_json_error(['message' => 'Update failed or no changes']);
275 + }
276 +}
277 +
278 +
279 +public function mxchat_display_admin_notice() {
280 + // Success notice
281 + if ($message = get_transient('mxchat_admin_notice_success')) {
282 + ?>
283 + <div class="notice notice-success is-dismissible">
284 + <p><?php echo esc_html($message); ?></p>
285 + <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
286 + </div>
287 + <?php
288 + delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
289 + }
290 +
291 + // Error notice
292 + if ($message = get_transient('mxchat_admin_notice_error')) {
293 + ?>
294 + <div class="notice notice-error is-dismissible">
295 + <p><?php echo esc_html($message); ?></p>
296 + <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
297 + </div>
298 + <?php
299 + delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
300 + }
301 +}
302 +
303 +
304 +
305 +
306 +
307 +public function mxchat_create_admin_page() {
308 + ?>
309 + <div class="wrap mxchat-admin">
310 + <?php if (!$this->is_activated): ?>
311 + <div class="mxchat-pro-banner">
312 + <p>
313 + For a limited time, get lifetime access and save $20 on MxChat Pro!
314 + <a href="https://mxchat.ai/" target="_blank">Upgrade to Pro today</a>
315 + </p>
316 + </div>
317 + <?php endif; ?>
318 +
319 + <div class="mxchat-agents-banner">
320 + <p>
321 + Pro users get exclusive access to <a href="https://mxchat.ai/agents/" target="_blank">MxChat AI Agents</a> – our groundbreaking platform to test, refine, and deploy your chatbot with confidence. Take your AI assistant to the next level!
322 + </p>
323 + </div>
324 +
325 + <h2 class="mxchat-nav-tab-wrapper">
326 + <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot">Chatbot</a>
327 + <a href="#embed" class="mxchat-nav-tab" data-tab="embed">Integrations</a>
328 + <a href="#theme" class="mxchat-nav-tab" data-tab="theme">Theme</a>
329 + <a href="#general" class="mxchat-nav-tab" data-tab="general">FAQ</a>
330 + </h2>
331 +
332 + <div id="chatbot" class="mxchat-tab-content active">
333 + <div class="mxchat-autosave-section">
334 + <?php do_settings_sections('mxchat-chatbot'); ?>
335 + </div>
336 + </div>
337 +
338 + <div id="embed" class="mxchat-tab-content">
339 + <div class="mxchat-autosave-section">
340 + <div class="mxchat-settings-section">
341 + <h2>WooCommerce Settings</h2>
342 + <table class="form-table">
343 + <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
344 + </table>
345 + </div>
346 + <div class="section-divider"></div>
347 +
348 + <div class="mxchat-settings-section">
349 + <h2>Loops Settings</h2>
350 + <table class="form-table">
351 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
352 + </table>
353 + </div>
354 +
355 + <div class="section-divider"></div>
356 +
357 + <div class="mxchat-settings-section">
358 + <h2>Brave Search Settings</h2>
359 + <table class="form-table">
360 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
361 + </table>
362 + </div>
363 +
364 + <div class="section-divider"></div>
365 +
366 + <div class="mxchat-settings-section">
367 + <h2>Toolbar Settings & Intents</h2>
368 + <table class="form-table">
369 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
370 + </table>
371 + </div>
372 + </div>
373 + </div>
374 +
375 + <div id="theme" class="mxchat-tab-content">
376 + <div class="mxchat-autosave-section">
377 + <?php do_settings_sections('mxchat-theme'); ?>
378 + </div>
379 + </div>
380 +
381 +<div id="general" class="mxchat-tab-content">
382 + <?php do_settings_sections('mxchat-general'); ?>
383 +<p>
384 + If you’re having trouble with setup or getting the responses you need, we encourage you to review our
385 + <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer">documentation</a> or
386 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer">create a support ticket</a>.
387 +</p>
388 +<p>
389 + 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>.
390 +</p>
391 +
392 +<div class="faq-item">
393 + <h3>How does the Claude API integration work?</h3>
394 + <p>
395 + 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.
396 + </p>
397 + <p>
398 + 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.
399 + </p>
400 + <p>
401 + 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>.
402 + </p>
403 +</div>
404 +
405 +<div class="faq-item">
406 + <h3>How does the X.AI API integration work?</h3>
407 + <p>
408 + 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.
409 + </p>
410 + <p>
411 + 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>.
412 + </p>
413 +</div>
414 +
415 +<div class="faq-item">
416 + <h3>Do I need an OpenAI API key to use the chatbot?</h3>
417 + <p>
418 + 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.
419 + </p>
420 + <p>
421 + 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.
422 + </p>
423 +</div>
424 +
425 + <div class="faq-item">
426 + <h3>How do I add the chatbot to my site?</h3>
427 + <p>
428 + 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.
429 + </p>
430 + </div>
431 +
432 + <div class="faq-item">
433 + <h3>How does the chatbot use my content to generate responses?</h3>
434 + <p>
435 + 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.
436 + </p>
437 + </div>
438 +
439 + <div class="faq-item">
440 + <h3>Why does the chatbot sometimes make up links or information?</h3>
441 + <p>
442 + 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.
443 + </p>
444 + </div>
445 +
446 + <div class="faq-item">
447 + <h3>How do intents work in MxChat?</h3>
448 + <p>
449 + 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>.
450 + </p>
451 + </div>
452 +
453 +
454 + <div class="faq-item">
455 + <h3>How does the Complianz integration work?</h3>
456 + <p>
457 + 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.
458 + </p>
459 + </div>
460 +
461 + <div class="faq-item">
462 + <h3>How does the WooCommerce integration work?</h3>
463 + <p>
464 + 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.
465 + </p>
466 + </div>
467 +
468 + <div class="faq-item">
469 + <h3>Why isn't my chatbot responding as expected?</h3>
470 + <p>
471 + 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.
472 + </p>
473 + <p>
474 + We’re dedicated to your success and ready to guide you in aligning the AI's behavior to meet your goals.
475 + </p>
476 + </div>
477 +
478 + <div class="faq-item">
479 + <h3>What is Loops, and how do I get an API key?</h3>
480 + <p>
481 + 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.
482 + </p>
483 + </div>
484 +
485 +
486 + </div>
487 + </div>
488 + <?php
489 +}
490 +
491 +
492 +
493 +public function mxchat_create_transcripts_page() {
494 + global $wpdb;
495 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
496 +
497 + // Get basic stats
498 + $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
499 + $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
500 + $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
501 + ?>
502 + <div class="wrap mxchat-admin">
503 + <!-- Stats Cards -->
504 + <div class="mxchat-stats-grid">
505 + <div class="mxchat-stat-card">
506 + <div class="stat-icon">💬</div>
507 + <div class="stat-content">
508 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
509 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
510 + </div>
511 + </div>
512 + <div class="mxchat-stat-card">
513 + <div class="stat-icon">📝</div>
514 + <div class="stat-content">
515 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
516 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
517 + </div>
518 + </div>
519 + <div class="mxchat-stat-card">
520 + <div class="stat-icon">👥</div>
521 + <div class="stat-content">
522 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
523 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
524 + </div>
525 + </div>
526 + </div>
527 +
528 + <!-- Search and Filter Controls -->
529 + <div class="mxchat-controls-wrapper">
530 + <div class="mxchat-search-box">
531 + <input type="text" id="mxchat-search-transcripts"
532 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
533 + class="regular-text">
534 + </div>
535 +
536 + <form id="mxchat-delete-form" method="post">
537 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
538 + <div class="mxchat-controls">
539 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
540 + <span class="dashicons dashicons-yes-alt"></span>
541 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
542 + </button>
543 + <button type="submit" class="button delete-chats-button">
544 + <span class="dashicons dashicons-trash"></span>
545 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
546 + </button>
547 + </div>
548 + </form>
549 + </div>
550 +
551 + <!-- Transcripts Container -->
552 + <div id="mxchat-transcripts"></div>
553 + </div>
554 + <?php
555 + }
556 +
557 +
558 +
559 +
560 +public function mxchat_create_prompts_page() {
561 + global $wpdb;
562 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
563 +
564 + // Display success message if all prompts were deleted
565 + if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
566 + echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
567 + }
568 +
569 + // Set up pagination and search query
570 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
571 + $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
572 + $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
573 + $per_page = 10;
574 + $offset = ($current_page - 1) * $per_page;
575 +
576 + // Modify query to handle search input
577 + $sql_search = "";
578 + if ($search_query) {
579 + $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
580 + }
581 +
582 + // Retrieve total number of prompts, considering search filter
583 + $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
584 + $total_pages = ceil($total_prompts / $per_page);
585 +
586 + // Retrieve prompts from the database
587 + $prompts = $wpdb->get_results(
588 + $wpdb->prepare(
589 + "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
590 + $per_page,
591 + $offset
592 + )
593 + );
594 +
595 + ?>
596 +
597 + <div class="wrap mxchat-admin">
598 + <div class="mxchat-grid-container">
599 + <!-- Submit Content Form -->
600 + <div class="mxchat-grid-item">
601 + <h2>Submit Content</h2>
602 + <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
603 + <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
604 + <div class="mxchat-form-group">
605 + <label for="article_content">Article Content:</label>
606 + <textarea name="article_content" id="article_content" required></textarea>
607 + </div>
608 + <div class="mxchat-form-group">
609 + <label for="article_url">Article URL (Optional):</label>
610 + <input type="url" name="article_url" id="article_url" placeholder="Enter related URL for the content">
611 + </div>
612 + <input type="submit" name="submit_content" value="Submit Content" class="button button-primary submit-content-button" />
613 + </form>
614 + <div id="mxchat-content-loading" class="mxchat-content-spinner" style="display: none;"></div>
615 + <div id="mxchat-content-loading-text" class="mxchat-loading-text">Submitting content, please wait...</div>
616 + </div>
617 +
618 +
619 +<!-- Combined Knowledge Import Settings -->
620 +<div class="mxchat-grid-item">
621 + <div class="mxchat-flex-section">
622 + <h2><?php esc_html_e('Content Import Settings', 'mxchat'); ?></h2>
623 +
624 + <div class="mxchat-settings-row">
625 + <!-- Auto-Sync Toggle Section -->
626 +<div class="mxchat-sync-settings">
627 + <form method="post" action="options.php" class="mxchat-sync-settings-form">
628 + <?php
629 + settings_fields('mxchat_prompts_options');
630 + $posts_sync = get_option('mxchat_auto_sync_posts');
631 + $pages_sync = get_option('mxchat_auto_sync_pages');
632 + ?>
633 + <div class="mxchat-toggle-group">
634 + <div class="mxchat-toggle-container">
635 + <label class="mxchat-toggle-switch">
636 + <input type="checkbox"
637 + name="mxchat_auto_sync_posts"
638 + value="1"
639 + <?php checked($posts_sync, '1'); ?>>
640 + <span class="mxchat-toggle-slider"></span>
641 + </label>
642 + <span class="mxchat-toggle-label">
643 + <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
644 + </span>
645 + </div>
646 +
647 + <div class="mxchat-toggle-container">
648 + <label class="mxchat-toggle-switch">
649 + <input type="checkbox"
650 + name="mxchat_auto_sync_pages"
651 + value="1"
652 + <?php checked($pages_sync, '1'); ?>>
653 + <span class="mxchat-toggle-slider"></span>
654 + </label>
655 + <span class="mxchat-toggle-label">
656 + <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
657 + </span>
658 + </div>
659 + <div class="mxchat-submit-wrapper">
660 + <?php submit_button('Save Settings', 'save-button-settings', 'submit', false); ?>
661 + </div>
662 + </div>
663 + </form>
664 +</div>
665 +
666 + <!-- URL Import Section with Processing Status -->
667 + <div class="mxchat-import-section">
668 + <?php
669 + // Check for any active processing
670 + $pdf_url = get_transient('mxchat_last_pdf_url');
671 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
672 + $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
673 + $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
674 +
675 + // Clear old completed statuses
676 + if ($pdf_status && $pdf_status['status'] === 'complete') {
677 + delete_transient('mxchat_last_pdf_url');
678 + $pdf_status = false;
679 + }
680 + if ($sitemap_status && $sitemap_status['status'] === 'complete') {
681 + delete_transient('mxchat_last_sitemap_url');
682 + $sitemap_status = false;
683 + }
684 +
685 + $is_processing = ($pdf_status && $pdf_status['status'] === 'processing') ||
686 + ($sitemap_status && $sitemap_status['status'] === 'processing');
687 + ?>
688 +
689 + <!-- Import Description -->
690 + <div class="mxchat-import-description">
691 + <p class="description-text">
692 + Import content from various sources:
693 + </p>
694 + <ul class="import-options">
695 + <li><strong>Sitemap URL:</strong> Add all pages from your sitemap</li>
696 + <li><strong>PDF URL:</strong> Import content from an online PDF</li>
697 + <li><strong>Single URL:</strong> Import from any webpage</li>
698 + </ul>
699 + </div>
700 +
701 + <!-- URL Import Form -->
702 + <?php if (!$is_processing) : ?>
703 + <form id="mxchat-sitemap-form" method="post" class="mxchat-sitemap-form"
704 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
705 + <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
706 + <div class="mxchat-url-input-group">
707 + <input type="url"
708 + name="sitemap_url"
709 + id="sitemap_url"
710 + placeholder="<?php esc_attr_e('Enter URL (Sitemap, PDF, or webpage)', 'mxchat'); ?>"
711 + required
712 + aria-label="<?php esc_attr_e('URL Input', 'mxchat'); ?>"
713 + />
714 + <input type="submit"
715 + name="submit_sitemap"
716 + value="<?php esc_attr_e('Import', 'mxchat'); ?>"
717 + class="button button-primary"
718 + />
719 + </div>
720 + </form>
721 + <?php endif; ?>
722 +
723 + <!-- Loading Indicators -->
724 + <div id="mxchat-sitemap-loading" class="mxchat-spinner" style="display: none;"
725 + aria-hidden="true"></div>
726 + <div id="mxchat-loading-text" class="screen-reader-text" style="display: none;">
727 + <?php esc_html_e('Loading information into database, please wait...', 'mxchat'); ?>
728 + </div>
729 +
730 + <!-- PDF Processing Status -->
731 + <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
732 + <div class="mxchat-process-status pdf" role="alert" aria-live="polite">
733 + <h3><?php esc_html_e('PDF Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
734 + <p><?php printf(esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
735 + absint($pdf_status['processed_pages']),
736 + absint($pdf_status['total_pages']),
737 + absint($pdf_status['percentage']));
738 + ?></p>
739 + <p><?php printf(esc_html__('Status: %s', 'mxchat'),
740 + esc_html(ucfirst($pdf_status['status'])));
741 + ?></p>
742 + <p><?php printf(esc_html__('Last update: %s', 'mxchat'),
743 + esc_html($pdf_status['last_update']));
744 + ?></p>
745 + </div>
746 + <?php endif; ?>
747 +
748 + <!-- Sitemap Processing Status -->
749 + <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
750 + <div class="mxchat-process-status sitemap" role="alert" aria-live="polite">
751 + <h3><?php esc_html_e('Sitemap Processing Status (Refresh page for update)', 'mxchat'); ?></h3>
752 + <p><?php printf(esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
753 + absint($sitemap_status['processed_urls']),
754 + absint($sitemap_status['total_urls']),
755 + absint($sitemap_status['percentage']));
756 + ?></p>
757 + <p><?php printf(esc_html__('Status: %s', 'mxchat'),
758 + esc_html(ucfirst($sitemap_status['status'])));
759 + ?></p>
760 + <p><?php printf(esc_html__('Last update: %s', 'mxchat'),
761 + esc_html($sitemap_status['last_update']));
762 + ?></p>
763 + </div>
764 + <?php endif; ?>
765 +
766 + <!-- Stop Processing Button -->
767 + <?php if ($is_processing) : ?>
768 + <form id="mxchat-stop-form" method="post" class="mxchat-stop-form"
769 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
770 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
771 + <input type="submit"
772 + name="stop_processing"
773 + value="<?php esc_attr_e('Stop Processing', 'mxchat'); ?>"
774 + class="button button-secondary"
775 + />
776 + </form>
777 + <?php endif; ?>
778 + </div>
779 + </div>
780 + </div>
781 +</div>
782 +
783 +</div>
784 +
785 +<!-- Knowledge Base Management Section -->
786 +<div class="mxchat-knowledge-container">
787 + <!-- Left Column: Management Controls -->
788 + <div class="mxchat-knowledge-controls">
789 + <div class="mxchat-controls-header">
790 + <span class="displaying-num"><?php echo esc_html($total_prompts); ?> items</span>
791 + </div>
792 +
793 + <?php
794 + // Generate pagination links
795 + $page_links = paginate_links(array(
796 + 'base' => add_query_arg(array(
797 + 'paged' => '%#%',
798 + 'search' => urlencode($search_query),
799 + '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
800 + ), admin_url('admin.php?page=mxchat-prompts')),
801 + 'format' => '',
802 + 'prev_text' => __('&laquo; Previous'),
803 + 'next_text' => __('Next &raquo;'),
804 + 'total' => $total_pages,
805 + 'current' => $current_page,
806 + ));
807 +
808 + if ($page_links) : ?>
809 + <div class="mxchat-pagination">
810 + <?php echo wp_kses_post($page_links); ?>
811 + </div>
812 + <?php endif; ?>
813 +
814 +
815 + <form method="post"
816 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
817 + class="delete-form"
818 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all prompts? This action cannot be undone.', 'mxchat'); ?>');">
819 + <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
820 + <input type="submit"
821 + name="delete_all_prompts"
822 + value="<?php esc_attr_e('Delete All Knowledge', 'mxchat'); ?>"
823 + class="button button-link-delete mxchat-delete-all" />
824 + </form>
825 +
826 + </div>
827 +
828 + <!-- Right Column: Search -->
829 + <div class="mxchat-knowledge-search">
830 + <form method="get" id="knowledge-search" class="search-form">
831 + <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
832 + <input type="hidden" name="page" value="mxchat-prompts" />
833 + <div class="mxchat-search-group">
834 + <input type="text"
835 + name="search"
836 + placeholder="Search Knowledge"
837 + value="<?php echo esc_attr($search_query); ?>"
838 + class="search-input" />
839 + <button type="submit" class="button button-primary search-button">
840 + <span class="dashicons dashicons-search"></span>
841 + Search
842 + </button>
843 + </div>
844 + </form>
845 + </div>
846 +</div>
847 +
848 +
849 + <!-- Prompts Table -->
850 + <table class="mxchat-table">
851 + <thead>
852 + <tr>
853 + <th>ID</th>
854 + <th>Article Content</th>
855 + <th>URL</th>
856 + <th>Actions</th>
857 + </tr>
858 + </thead>
859 + <tbody>
860 + <?php if ($prompts) : ?>
861 + <?php foreach ($prompts as $prompt) : ?>
862 + <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
863 + <td><?php echo esc_html($prompt->id); ?></td>
864 + <td class="mxchat_article_content_dashboard">
865 + <span class="content-view"><?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?></span>
866 + <textarea class="content-edit" style="display:none;"><?php echo esc_textarea($prompt->article_content); ?></textarea>
867 + </td>
868 + <td class="mxchat_article_url_dashboard">
869 + <span class="url-view">
870 + <?php if (!empty($prompt->source_url)) : ?>
871 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank"><?php echo esc_html($prompt->source_url); ?></a>
872 + <?php else : ?>
873 + N/A
874 + <?php endif; ?>
875 + </span>
876 + <input type="url" class="url-edit" value="<?php echo esc_attr($prompt->source_url); ?>" style="display:none;" />
877 + </td>
878 + <td>
879 + <button class="button edit-button" data-id="<?php echo esc_attr($prompt->id); ?>">Edit</button>
880 + <button class="button save-button" data-id="<?php echo esc_attr($prompt->id); ?>" style="display:none;">Save</button>
881 + <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>
882 + </td>
883 + </tr>
884 + <?php endforeach; ?>
885 + <?php else : ?>
886 + <tr>
887 + <td colspan="4"><?php esc_html_e('No prompts found.', 'mxchat'); ?></td>
888 + </tr>
889 + <?php endif; ?>
890 + </tbody>
891 + </table>
892 + </div>
893 + <?php
894 +}
895 +public function mxchat_handle_delete_all_prompts() {
896 + // Verify nonce
897 + if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
898 + wp_die(__('Nonce verification failed.', 'mxchat'));
899 + }
900 +
901 + // Check permissions
902 + if (!current_user_can('manage_options')) {
903 + wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
904 + }
905 +
906 + global $wpdb;
907 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
908 +
909 + // Delete all prompts from the table
910 + $wpdb->query("DELETE FROM {$table_name}");
911 +
912 + // Clear relevant cache
913 + wp_cache_delete('all_prompts', 'mxchat_prompts');
914 +
915 + // Redirect back with a success message
916 + $redirect_url = add_query_arg(array(
917 + 'page' => 'mxchat-prompts',
918 + 'all_deleted' => 'true'
919 + ), admin_url('admin.php'));
920 +
921 + wp_safe_redirect($redirect_url);
922 + exit;
923 +}
924 +public function mxchat_handle_delete_prompt() {
925 + // Sanitize and validate nonce
926 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
927 + if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
928 + wp_die('Nonce verification failed.');
929 + }
930 +
931 + // Check permissions
932 + if (!current_user_can('manage_options')) {
933 + wp_die('You do not have sufficient permissions to delete prompts.');
934 + }
935 +
936 + // Validate and sanitize ID parameter
937 + $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
938 + if ($id <= 0) {
939 + wp_die('Invalid prompt ID.');
940 + }
941 +
942 + global $wpdb;
943 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
944 +
945 + // Clear cache and delete prompt
946 + wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
947 + $wpdb->delete($table_name, array('id' => $id), array('%d'));
948 +
949 + wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
950 + exit;
951 +}
952 +public function mxchat_generate_embedding($text) {
953 + $options = get_option('mxchat_options');
954 + $api_key = $options['api_key'] ?? 'default_api_key';
955 +
956 + $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
957 + 'body' => wp_json_encode(array(
958 + 'model' => 'text-embedding-ada-002',
959 + 'input' => $text
960 + )),
961 + 'headers' => array(
962 + 'Authorization' => 'Bearer ' . $api_key,
963 + 'Content-Type' => 'application/json'
964 + ),
965 + ));
966 +
967 + if (is_wp_error($response)) {
968 + return null;
969 + }
970 +
971 + $response_data = json_decode(wp_remote_retrieve_body($response), true);
972 + return $response_data['data'][0]['embedding'] ?? null;
973 + }
974 +public function mxchat_delete_chat_history() {
975 + if (!current_user_can('manage_options')) {
976 + echo wp_json_encode(['error' => 'You do not have sufficient permissions.']);
977 + wp_die();
978 + }
979 +
980 + check_ajax_referer('mxchat_delete_chat_history', 'security');
981 +
982 + global $wpdb;
983 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
984 +
985 + if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
986 + foreach ($_POST['delete_session_ids'] as $session_id) {
987 + $session_id_sanitized = sanitize_text_field($session_id);
988 +
989 + // Clear relevant cache before deletion
990 + $cache_key = 'chat_session_' . $session_id_sanitized;
991 + wp_cache_delete($cache_key, 'mxchat_chat_sessions');
992 +
993 + // Perform the deletion
994 + $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
995 + }
996 +
997 + // Optionally, clear a general cache if you have one
998 + wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
999 +
1000 + echo wp_json_encode(['success' => 'Selected chat sessions have been deleted.']);
1001 + } else {
1002 + echo wp_json_encode(['error' => 'No chat sessions selected for deletion.']);
1003 + }
1004 +
1005 + wp_die();
1006 + }
1007 +public function mxchat_save_inline_prompt() {
1008 + // Check for nonce security
1009 + check_ajax_referer('mxchat_save_inline_nonce');
1010 +
1011 + // Verify permissions
1012 + if (!current_user_can('manage_options')) {
1013 + wp_send_json_error('Permission denied.');
1014 + return;
1015 + }
1016 +
1017 + global $wpdb;
1018 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1019 +
1020 + // Validate and sanitize input data
1021 + $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1022 + $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1023 + $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1024 +
1025 + if ($prompt_id > 0 && !empty($article_content)) {
1026 + // Re-generate the embedding vector for the updated content
1027 + $embedding_vector = $this->mxchat_generate_embedding($article_content);
1028 +
1029 + if (is_array($embedding_vector)) {
1030 + // Serialize the embedding vector before storing it
1031 + $embedding_vector_serialized = serialize($embedding_vector);
1032 +
1033 + // Update the prompt in the database
1034 + $updated = $wpdb->update(
1035 + $table_name,
1036 + array(
1037 + 'article_content' => $article_content,
1038 + 'embedding_vector' => $embedding_vector_serialized,
1039 + 'source_url' => $article_url,
1040 + ),
1041 + array('id' => $prompt_id),
1042 + array('%s', '%s', '%s'),
1043 + array('%d')
1044 + );
1045 +
1046 + if ($updated !== false) {
1047 + wp_send_json_success();
1048 + } else {
1049 + wp_send_json_error('Database update failed.');
1050 + }
1051 + } else {
1052 + wp_send_json_error('Embedding generation failed.');
1053 + }
1054 + } else {
1055 + wp_send_json_error('Invalid data.');
1056 + }
1057 +}
1058 +// Add this method to handle post updates
1059 +public function handle_post_update($post_id, $post, $update) {
1060 + // Don't process auto-saves or revisions
1061 + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
1062 + //error_log('MXChat: Skipping - autosave');
1063 + return;
1064 + }
1065 + if (wp_is_post_revision($post_id)) {
1066 + //error_log('MXChat: Skipping - revision');
1067 + return;
1068 + }
1069 +
1070 + // Only process published content
1071 + if (!in_array($post->post_status, array('publish'))) {
1072 + //error_log('MXChat: Skipping - not published');
1073 + return;
1074 + }
1075 +
1076 + // Check if sync is enabled for this post type
1077 + $post_type = $post->post_type;
1078 + $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1079 +
1080 + if (get_option($sync_option) !== '1') {
1081 + //error_log('MXChat: Skipping - sync not enabled for this type');
1082 + return;
1083 + }
1084 +
1085 + // Only process posts and pages
1086 + if (!in_array($post_type, array('post', 'page'))) return;
1087 +
1088 + global $wpdb;
1089 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1090 +
1091 + // Get the post content and URL
1092 + $content = wp_strip_all_tags($post->post_content);
1093 + $url = get_permalink($post_id);
1094 +
1095 + // Check if this URL already exists in the database
1096 + $existing_entry = $wpdb->get_row(
1097 + $wpdb->prepare(
1098 + "SELECT id FROM $table_name WHERE source_url = %s",
1099 + $url
1100 + )
1101 + );
1102 +
1103 + // Generate embedding for the content
1104 + $embedding_vector = $this->mxchat_generate_embedding($content);
1105 + if (!$embedding_vector) return;
1106 +
1107 + if ($existing_entry) {
1108 + // Update existing entry
1109 + $wpdb->update(
1110 + $table_name,
1111 + array(
1112 + 'article_content' => $content,
1113 + 'embedding_vector' => serialize($embedding_vector),
1114 + 'timestamp' => current_time('mysql')
1115 + ),
1116 + array('id' => $existing_entry->id),
1117 + array('%s', '%s', '%s'),
1118 + array('%d')
1119 + );
1120 + } else {
1121 + // Insert new entry
1122 + $wpdb->insert(
1123 + $table_name,
1124 + array(
1125 + 'article_content' => $content,
1126 + 'source_url' => $url,
1127 + 'embedding_vector' => serialize($embedding_vector),
1128 + 'timestamp' => current_time('mysql')
1129 + ),
1130 + array('%s', '%s', '%s', '%s')
1131 + );
1132 + }
1133 +}
1134 +public function mxchat_handle_content_submission() {
1135 + // Check if the form was submitted and the user has sufficient permissions
1136 + if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1137 + return;
1138 + }
1139 +
1140 + // Verify the nonce field for security
1141 + $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1142 + if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1143 + wp_die('Nonce verification failed.');
1144 + }
1145 +
1146 + // Sanitize the content input
1147 + $article_content = sanitize_textarea_field($_POST['article_content']);
1148 +
1149 + // Sanitize the URL input (optional URL)
1150 + $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Default to empty if not provided
1151 +
1152 + // Generate the embedding vector for the content
1153 + $embedding_vector = $this->mxchat_generate_embedding($article_content);
1154 +
1155 + global $wpdb;
1156 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1157 +
1158 + // Check if the 'source_url' column exists in the table and add it if it doesn't
1159 + if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1160 + // Use wpdb::query and a prepared statement to avoid SQL injection
1161 + $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1162 + }
1163 +
1164 + if (is_array($embedding_vector)) {
1165 + // Serialize the embedding vector before storing it
1166 + $embedding_vector_serialized = serialize($embedding_vector);
1167 +
1168 + // Insert the content, embedding vector, and source URL into the database, using a prepared statement
1169 + $inserted = $wpdb->insert(
1170 + $table_name,
1171 + array(
1172 + 'article_content' => $article_content,
1173 + 'embedding_vector' => $embedding_vector_serialized,
1174 + 'source_url' => $article_url, // Insert the URL, empty string if not provided
1175 + ),
1176 + array(
1177 + '%s', // Format for article_content (string)
1178 + '%s', // Format for embedding_vector (serialized string)
1179 + '%s', // Format for source_url (string)
1180 + )
1181 + );
1182 +
1183 + if ($inserted === false) {
1184 + //error_log('Error inserting content: ' . $wpdb->last_error);
1185 + set_transient('mxchat_admin_notice_error', 'Error inserting content into the database. Please try again.', 30);
1186 + } else {
1187 + set_transient('mxchat_admin_notice_success', 'Content successfully submitted!', 30);
1188 + }
1189 +
1190 + } else {
1191 + //error_log('Embedding generation failed for article content: ' . $article_content);
1192 + set_transient('mxchat_admin_notice_error', 'Embedding generation failed. Please ensure your API key is correct and try again.', 30);
1193 + }
1194 +
1195 + // Redirect after setting the transient
1196 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1197 + exit;
1198 +}
1199 +private function is_pdf_url($url, $response) {
1200 + $content_type = wp_remote_retrieve_header($response, 'content-type');
1201 + $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1202 +
1203 + return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1204 +}
1205 +private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1206 + if (!current_user_can('manage_options')) {
1207 + //error_log('Unauthorized PDF processing attempt');
1208 + return false;
1209 + }
1210 +
1211 + $pdf_url = esc_url_raw($pdf_url);
1212 + $upload_dir = wp_upload_dir();
1213 +
1214 + if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1215 + //error_log(sprintf('Upload directory error: %s', esc_html($upload_dir['error'])));
1216 + return false;
1217 + }
1218 +
1219 + $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1220 + $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1221 +
1222 + $response_body = wp_remote_retrieve_body($response);
1223 + if (empty($response_body)) {
1224 + //error_log('Empty PDF response body');
1225 + return false;
1226 + }
1227 +
1228 + if (!wp_mkdir_p(dirname($pdf_path))) {
1229 + //error_log(sprintf('Failed to create directory for PDF: %s', esc_html($pdf_path)));
1230 + return false;
1231 + }
1232 +
1233 + try {
1234 + file_put_contents($pdf_path, $response_body);
1235 +
1236 + if (!file_exists($pdf_path)) {
1237 + throw new Exception('Failed to save PDF file');
1238 + }
1239 +
1240 + $parser = new \Smalot\PdfParser\Parser();
1241 + $pdf = $parser->parseFile($pdf_path);
1242 + $total_pages = absint(count($pdf->getPages()));
1243 +
1244 + if ($total_pages < 1) {
1245 + throw new Exception('Invalid PDF: no pages found');
1246 + }
1247 +
1248 + wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1249 + 'pdf_path' => $pdf_path,
1250 + 'pdf_url' => $pdf_url,
1251 + 'total_pages' => $total_pages,
1252 + 'batch_size' => absint(15),
1253 + 'batch_pause' => absint(10)
1254 + ));
1255 +
1256 + $status_data = array(
1257 + 'total_pages' => $total_pages,
1258 + 'processed_pages' => 0,
1259 + 'status' => 'processing',
1260 + 'last_update' => time()
1261 + );
1262 +
1263 + set_transient(
1264 + sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1265 + array_map('sanitize_text_field', $status_data),
1266 + DAY_IN_SECONDS
1267 + );
1268 +
1269 + return 'scheduled';
1270 +
1271 + } catch (Exception $e) {
1272 + //error_log(sprintf('Error preparing PDF for processing: %s', esc_html($e->getMessage())));
1273 + if (file_exists($pdf_path)) {
1274 + wp_delete_file($pdf_path);
1275 + }
1276 + return false;
1277 + }
1278 +}
1279 +public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1280 + // Validate inputs
1281 + $pdf_path = sanitize_text_field($pdf_path);
1282 + $pdf_url = esc_url_raw($pdf_url);
1283 + $total_pages = absint($total_pages);
1284 + $batch_size = absint($batch_size);
1285 + $batch_pause = absint($batch_pause);
1286 +
1287 + try {
1288 + if (!file_exists($pdf_path)) {
1289 + throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1290 + }
1291 +
1292 + $parser = new \Smalot\PdfParser\Parser();
1293 + $pdf = $parser->parseFile($pdf_path);
1294 + $pages = $pdf->getPages();
1295 +
1296 + // Get current progress
1297 + $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1298 + $status = get_transient($status_key);
1299 +
1300 + if (!$status || !is_array($status)) {
1301 + throw new Exception('Invalid status data retrieved from transient');
1302 + }
1303 +
1304 + $start_page = absint($status['processed_pages']);
1305 + $end_page = min($start_page + $batch_size, $total_pages);
1306 +
1307 + $instance = new self(); // Create an instance of the class
1308 +
1309 + for ($i = $start_page; $i < $end_page; $i++) {
1310 + $text = $pages[$i]->getText();
1311 + $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1312 +
1313 + if (!empty($sanitized_content)) {
1314 + $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1315 + if (is_array($embedding_vector)) {
1316 + $metadata = array(
1317 + 'document_type' => 'pdf',
1318 + 'total_pages' => $total_pages,
1319 + 'current_page' => $i + 1,
1320 + 'prev_page' => $i > 0 ? $i : null,
1321 + 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1322 + 'source_url' => $pdf_url
1323 + );
1324 +
1325 + $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1326 + $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1327 +
1328 + $options = get_option('mxchat_options');
1329 + MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1330 + }
1331 + }
1332 +
1333 + // Update progress with sanitized data
1334 + $status['processed_pages'] = absint($i + 1);
1335 + $status['last_update'] = time();
1336 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1337 + }
1338 +
1339 + // Schedule next batch if needed
1340 + if ($end_page < $total_pages) {
1341 + wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
1342 + 'pdf_path' => $pdf_path,
1343 + 'pdf_url' => $pdf_url,
1344 + 'total_pages' => $total_pages,
1345 + 'batch_size' => $batch_size,
1346 + 'batch_pause' => $batch_pause
1347 + ));
1348 + } else {
1349 + // Processing complete
1350 + $status['status'] = 'complete';
1351 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1352 + if (file_exists($pdf_path)) {
1353 + wp_delete_file($pdf_path);
1354 + }
1355 + }
1356 +
1357 + } catch (\Exception $e) {
1358 + $status['status'] = 'error';
1359 + $status['error'] = sanitize_text_field($e->getMessage());
1360 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1361 + if (file_exists($pdf_path)) {
1362 + wp_delete_file($pdf_path);
1363 + }
1364 + }
1365 +}
1366 +public function get_pdf_processing_status($pdf_url) {
1367 + $pdf_url = esc_url_raw($pdf_url);
1368 + $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1369 +
1370 + if (!$status || !is_array($status)) {
1371 + return false;
1372 + }
1373 +
1374 + return array(
1375 + 'total_pages' => absint($status['total_pages']),
1376 + 'processed_pages' => absint($status['processed_pages']),
1377 + 'percentage' => round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100),
1378 + 'status' => sanitize_text_field($status['status']),
1379 + 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
1380 + );
1381 +}
1382 +public function mxchat_handle_sitemap_submission() {
1383 + // Check if the form was submitted and verify permissions
1384 + if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
1385 + wp_die(esc_html__('Unauthorized access', 'mxchat'));
1386 + }
1387 +
1388 + // Verify nonce
1389 + check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1390 +
1391 + // Validate URL
1392 + if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
1393 + set_transient('mxchat_admin_notice_error',
1394 + esc_html__('Please provide a valid URL.', 'mxchat'),
1395 + 30
1396 + );
1397 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1398 + exit;
1399 + }
1400 +
1401 + $submitted_url = esc_url_raw($_POST['sitemap_url']);
1402 + $response = wp_remote_get($submitted_url, array('timeout' => 30));
1403 +
1404 + if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1405 + $error_message = is_wp_error($response) ? $response->get_error_message() : 'Failed to fetch URL';
1406 + set_transient('mxchat_admin_notice_error',
1407 + sprintf(
1408 + esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1409 + esc_html($error_message)
1410 + ),
1411 + 30
1412 + );
1413 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1414 + exit;
1415 + }
1416 +
1417 + $content_type = wp_remote_retrieve_header($response, 'content-type');
1418 + $body_content = wp_remote_retrieve_body($response);
1419 +
1420 + if (empty($body_content)) {
1421 + set_transient('mxchat_admin_notice_error',
1422 + esc_html__('Empty response received from URL.', 'mxchat'),
1423 + 30
1424 + );
1425 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1426 + exit;
1427 + }
1428 +
1429 + // Handle PDF URL
1430 + if ($this->is_pdf_url($submitted_url, $response)) {
1431 + $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
1432 +
1433 + if ($result === 'scheduled') {
1434 + set_transient(
1435 + 'mxchat_last_pdf_url',
1436 + sanitize_text_field($submitted_url),
1437 + DAY_IN_SECONDS
1438 + );
1439 + set_transient('mxchat_admin_notice_info',
1440 + esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1441 + 30
1442 + );
1443 + } else {
1444 + set_transient('mxchat_admin_notice_error',
1445 + esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
1446 + 30
1447 + );
1448 + }
1449 +
1450 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1451 + exit;
1452 + }
1453 +
1454 + // Handle Sitemap XML
1455 + if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
1456 + libxml_use_internal_errors(true);
1457 + $xml = simplexml_load_string($body_content);
1458 + $xml_errors = libxml_get_errors();
1459 + libxml_clear_errors();
1460 +
1461 + if ($xml === false || !empty($xml_errors)) {
1462 + set_transient('mxchat_admin_notice_error',
1463 + esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1464 + 30
1465 + );
1466 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1467 + exit;
1468 + }
1469 +
1470 + $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
1471 +
1472 + if ($result === 'scheduled') {
1473 + set_transient(
1474 + 'mxchat_last_sitemap_url',
1475 + sanitize_text_field($submitted_url),
1476 + DAY_IN_SECONDS
1477 + );
1478 + set_transient('mxchat_admin_notice_info',
1479 + esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1480 + 30
1481 + );
1482 + } else {
1483 + set_transient('mxchat_admin_notice_error',
1484 + esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
1485 + 30
1486 + );
1487 + }
1488 +
1489 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1490 + exit;
1491 + }
1492 +
1493 + // Handle Regular URL
1494 + $page_content = $this->mxchat_extract_main_content($body_content);
1495 + $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
1496 +
1497 + if (empty($sanitized_content)) {
1498 + set_transient('mxchat_admin_notice_error',
1499 + esc_html__('No valid content found on the provided URL.', 'mxchat'),
1500 + 30
1501 + );
1502 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1503 + exit;
1504 + }
1505 +
1506 + $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
1507 + if (is_array($embedding_vector)) {
1508 + MxChat_Utils::submit_content_to_db(
1509 + $sanitized_content,
1510 + $submitted_url,
1511 + $this->options['api_key']
1512 + );
1513 + set_transient('mxchat_admin_notice_success',
1514 + esc_html__('URL content successfully submitted!', 'mxchat'),
1515 + 30
1516 + );
1517 + } else {
1518 + set_transient('mxchat_admin_notice_error',
1519 + esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
1520 + 30
1521 + );
1522 + }
1523 +
1524 + wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1525 + exit;
1526 +}
1527 +private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
1528 + if (!current_user_can('manage_options')) {
1529 + //error_log('Unauthorized sitemap processing attempt');
1530 + return false;
1531 + }
1532 +
1533 + try {
1534 + $sitemap_url = esc_url_raw($sitemap_url);
1535 +
1536 + if (!$xml || !is_object($xml)) {
1537 + throw new Exception('Invalid XML object provided');
1538 + }
1539 +
1540 + $urls = [];
1541 + foreach ($xml->url as $url_element) {
1542 + $url = esc_url_raw((string)$url_element->loc);
1543 + if ($url) {
1544 + $urls[] = $url;
1545 + }
1546 + }
1547 +
1548 + $total_urls = absint(count($urls));
1549 +
1550 + if ($total_urls < 1) {
1551 + throw new Exception('No valid URLs found in sitemap');
1552 + }
1553 +
1554 + wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
1555 + 'urls' => $urls,
1556 + 'sitemap_url' => $sitemap_url,
1557 + 'total_urls' => $total_urls,
1558 + 'batch_size' => absint(10),
1559 + 'batch_pause' => absint(5)
1560 + ));
1561 +
1562 + $status_data = array(
1563 + 'total_urls' => $total_urls,
1564 + 'processed_urls' => 0,
1565 + 'status' => 'processing',
1566 + 'last_update' => time()
1567 + );
1568 +
1569 + set_transient(
1570 + sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
1571 + array_map('sanitize_text_field', $status_data),
1572 + DAY_IN_SECONDS
1573 + );
1574 +
1575 + return 'scheduled';
1576 +
1577 + } catch (\Exception $e) {
1578 + //error_log(sprintf('Error preparing sitemap for processing: %s', esc_html($e->getMessage())));
1579 + return false;
1580 + }
1581 +}
1582 +public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1583 + // Validate inputs
1584 + $sitemap_url = esc_url_raw($sitemap_url);
1585 + $total_urls = absint($total_urls);
1586 + $batch_size = absint($batch_size);
1587 + $batch_pause = absint($batch_pause);
1588 +
1589 + if (!is_array($urls) || empty($urls)) {
1590 + return;
1591 + }
1592 +
1593 + try {
1594 + $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
1595 + $status = get_transient($status_key);
1596 +
1597 + if (!$status || !is_array($status)) {
1598 + throw new Exception('Invalid status data retrieved from transient');
1599 + }
1600 +
1601 + $start_url = absint($status['processed_urls']);
1602 + $end_url = min($start_url + $batch_size, $total_urls);
1603 +
1604 + $instance = new self(); // Create an instance of the class
1605 + $embedding_success = true;
1606 +
1607 + for ($i = $start_url; $i < $end_url; $i++) {
1608 + $page_url = esc_url_raw($urls[$i]);
1609 + $page_response = wp_remote_get($page_url);
1610 +
1611 + if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1612 + continue;
1613 + }
1614 +
1615 + $page_html = wp_remote_retrieve_body($page_response);
1616 + $page_content = $instance->mxchat_extract_main_content($page_html); // Call via instance
1617 + $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content); // Call via instance
1618 +
1619 + if (!empty($sanitized_content)) {
1620 + $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1621 + if (is_array($embedding_vector)) {
1622 + $options = get_option('mxchat_options');
1623 + MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
1624 + } else {
1625 + $embedding_success = false;
1626 + }
1627 + }
1628 +
1629 + $status['processed_urls'] = absint($i + 1);
1630 + $status['last_update'] = time();
1631 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1632 + }
1633 +
1634 + if ($end_url < $total_urls) {
1635 + wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
1636 + 'urls' => array_map('esc_url_raw', $urls),
1637 + 'sitemap_url' => $sitemap_url,
1638 + 'total_urls' => $total_urls,
1639 + 'batch_size' => $batch_size,
1640 + 'batch_pause' => $batch_pause,
1641 + ));
1642 + } else {
1643 + $status['status'] = 'complete';
1644 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1645 + }
1646 + } catch (\Exception $e) {
1647 + $status['status'] = 'error';
1648 + $status['error'] = sanitize_text_field($e->getMessage());
1649 + set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1650 + }
1651 +}
1652 +public function get_sitemap_processing_status($sitemap_url) {
1653 + $sitemap_url = esc_url_raw($sitemap_url);
1654 + $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
1655 +
1656 + if (!$status || !is_array($status)) {
1657 + return false;
1658 + }
1659 +
1660 + return array(
1661 + 'total_urls' => absint($status['total_urls']),
1662 + 'processed_urls' => absint($status['processed_urls']),
1663 + 'percentage' => round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100),
1664 + 'status' => sanitize_text_field($status['status']),
1665 + 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ago'
1666 + );
1667 +}
1668 +public function mxchat_stop_processing() {
1669 + // Verify permissions
1670 + if (!current_user_can('manage_options')) {
1671 + wp_die(esc_html__('Unauthorized access', 'mxchat'));
1672 + }
1673 +
1674 + // Verify nonce
1675 + check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
1676 +
1677 + // Get the last sitemap URL and clear its transient
1678 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
1679 + if ($sitemap_url) {
1680 + delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
1681 + delete_transient('mxchat_last_sitemap_url');
1682 + }
1683 +
1684 + // Get the last PDF URL and clear its transient
1685 + $pdf_url = get_transient('mxchat_last_pdf_url');
1686 + if ($pdf_url) {
1687 + delete_transient('mxchat_pdf_status_' . md5($pdf_url));
1688 + delete_transient('mxchat_last_pdf_url');
1689 + }
1690 +
1691 + // Unschedule any pending sitemap events
1692 + $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
1693 + if ($timestamp) {
1694 + wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
1695 + }
1696 +
1697 + // Redirect back with a success message
1698 + set_transient('mxchat_admin_notice_success',
1699 + esc_html__('Processing has been stopped successfully.', 'mxchat'),
1700 + 30
1701 + );
1702 + wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
1703 + exit;
1704 +}
1705 +private function mxchat_sanitize_content_for_api($content) {
1706 + // Remove script, style tags, and HTML comments
1707 + $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
1708 + $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
1709 + $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
1710 +
1711 + // Remove all HTML tags and decode HTML entities
1712 + $content = wp_strip_all_tags($content);
1713 + $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
1714 +
1715 + // Trim and normalize whitespace
1716 + $content = trim(preg_replace('/\s+/', ' ', $content));
1717 +
1718 + return $content;
1719 +}
1720 +
1721 +
1722 +
1723 +
1724 +
1725 +
1726 +public function mxchat_fetch_chat_history() {
1727 + global $wpdb;
1728 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1729 +
1730 + if (!current_user_can('manage_options')) {
1731 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1732 + }
1733 +
1734 + // First get unique session IDs ordered by most recent message in each session
1735 + $session_ids = $wpdb->get_col(
1736 + $wpdb->prepare(
1737 + "SELECT DISTINCT session_id
1738 + FROM {$table_name}
1739 + GROUP BY session_id
1740 + ORDER BY MAX(timestamp) DESC"
1741 + )
1742 + );
1743 +
1744 + if (empty($session_ids)) {
1745 + wp_die(esc_html__('No chat history available.', 'mxchat'));
1746 + }
1747 +
1748 + ob_start();
1749 + echo '<div class="mxchat-transcript">';
1750 +
1751 + // Iterate through sessions from newest to oldest
1752 + foreach ($session_ids as $session_id) {
1753 + // Get the email associated with this session (if available)
1754 + $email = $wpdb->get_var(
1755 + $wpdb->prepare(
1756 + "SELECT user_email
1757 + FROM {$table_name}
1758 + WHERE session_id = %s AND user_email != ''
1759 + ORDER BY timestamp ASC
1760 + LIMIT 1",
1761 + $session_id
1762 + )
1763 + );
1764 +
1765 + // Debug: Check email retrieval
1766 + if (empty($email)) {
1767 + //error_log("No email found for session {$session_id}");
1768 + } else {
1769 + //error_log("Email for session {$session_id}: " . $email);
1770 + }
1771 +
1772 + // Get messages for this session ordered by timestamp
1773 + $messages = $wpdb->get_results(
1774 + $wpdb->prepare(
1775 + "SELECT * FROM {$table_name}
1776 + WHERE session_id = %s
1777 + ORDER BY timestamp ASC",
1778 + $session_id
1779 + )
1780 + );
1781 +
1782 + // Start session block
1783 + echo '<div class="mxchat-session">';
1784 + echo '<div class="mxchat-session-header">';
1785 + // Wrap checkbox and session ID in one block
1786 + echo '<div class="mxchat-session-id">';
1787 + echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
1788 + echo '<strong>Session ID:</strong> ' . esc_html($session_id);
1789 + echo '</div>';
1790 +
1791 + // Place email directly below the session ID block
1792 + if (!empty($email)) {
1793 + echo '<div class="mxchat-session-email">';
1794 + echo '<strong>Email:</strong> ' . esc_html($email);
1795 + echo '</div>';
1796 + }
1797 + echo '</div>';
1798 +
1799 + echo '<div class="mxchat-messages">';
1800 +
1801 + // Display messages for this session
1802 + foreach ($messages as $transcript) {
1803 + $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
1804 +
1805 + // Determine message styling
1806 + switch ($transcript->role) {
1807 + case 'assistant':
1808 + case 'bot':
1809 + $message_class = 'bot-message';
1810 + $display_role = esc_html__('Chatbot', 'mxchat');
1811 + break;
1812 + case 'user':
1813 + $message_class = 'user-message';
1814 + $display_role = !empty($transcript->user_identifier)
1815 + ? sanitize_text_field($transcript->user_identifier)
1816 + : esc_html__('User', 'mxchat');
1817 + break;
1818 + case 'agent':
1819 + $message_class = 'agent-message';
1820 + $display_role = esc_html__('Agent', 'mxchat');
1821 + break;
1822 + default:
1823 + $message_class = 'unknown-message';
1824 + $display_role = esc_html__('Unknown', 'mxchat');
1825 + }
1826 +
1827 + // Process message content
1828 + $message_content = wp_kses(
1829 + stripslashes($transcript->message),
1830 + [
1831 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1832 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1833 + 'a' => ['href' => [], 'title' => []]
1834 + ]
1835 + );
1836 + $message_content = nl2br($message_content);
1837 +
1838 + // Render message
1839 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1840 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1841 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1842 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1843 + echo '</div>';
1844 + }
1845 +
1846 + // Close session block
1847 + echo '</div></div>';
1848 + }
1849 +
1850 + echo '</div>';
1851 + $output = ob_get_clean();
1852 + echo $output;
1853 + wp_die();
1854 +}
1855 +
1856 +public function mxchat_create_activation_page() {
1857 + $license_status = get_option('mxchat_license_status', 'inactive');
1858 + $license_error = get_option('mxchat_license_error', '');
1859 + ?>
1860 + <div class="wrap mxchat-admin">
1861 + <h2>MxChat Pro: Activation</h2>
1862 + <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
1863 + <div class="error notice">
1864 + <p><?php echo esc_html($license_error); ?></p>
1865 + </div>
1866 + <?php endif; ?>
1867 +
1868 + <form id="mxchat-activation-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
1869 + <table class="form-table">
1870 + <tr valign="top">
1871 + <th scope="row">Email Address</th>
1872 + <td>
1873 + <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 />
1874 + </td>
1875 + </tr>
1876 + <tr valign="top">
1877 + <th scope="row">Activation Key</th>
1878 + <td>
1879 + <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 />
1880 + </td>
1881 + </tr>
1882 + </table>
1883 + <?php if ($license_status !== 'active'): ?>
1884 + <button type="submit" id="activate_license_button" class="button button-primary"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
1885 + <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
1886 + <?php endif; ?>
1887 + </form>
1888 +
1889 + <!-- License Status Display -->
1890 + <h3>License Status: <span id="mxchat-license-status"><?php echo $license_status === 'active' ? 'Active' : 'Inactive'; ?></span></h3>
1891 + </div>
1892 + <?php
1893 +}
1894 +
1895 +public function mxchat_intents_page_html() {
1896 + if ( ! current_user_can( 'manage_options' ) ) {
1897 + return;
1898 + }
1899 +
1900 + // Fetch existing intents with pagination and filtering
1901 + global $wpdb;
1902 + $table_name = $wpdb->prefix . 'mxchat_intents';
1903 + $page = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
1904 + $per_page = 20;
1905 + $offset = ( $page - 1 ) * $per_page;
1906 +
1907 +
1908 + // Add at the start of mxchat_intents_page_html()
1909 +if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
1910 + echo '<div class="notice notice-success is-dismissible"><p>' .
1911 + esc_html__('Intent updated successfully.', 'mxchat') .
1912 + '</p></div>';
1913 +}
1914 +
1915 + // Build the WHERE clause based on filters
1916 + $where = '1=1';
1917 + $search_term = isset( $_GET['s'] ) ? trim( $_GET['s'] ) : '';
1918 + $callback_filter = isset( $_GET['callback_filter'] ) ? sanitize_text_field( $_GET['callback_filter'] ) : '';
1919 +
1920 + if ( $search_term ) {
1921 + $search_term_like = '%' . $wpdb->esc_like( $search_term ) . '%';
1922 + $where .= $wpdb->prepare( ' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like );
1923 + }
1924 +
1925 + if ( $callback_filter ) {
1926 + $where .= $wpdb->prepare( ' AND callback_function = %s', $callback_filter );
1927 + }
1928 +
1929 + // Get total count for pagination
1930 + $total_intents = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE $where" );
1931 + $total_pages = ceil( $total_intents / $per_page );
1932 +
1933 + // Fetch intents with pagination and filters
1934 + $intents = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset ) );
1935 +
1936 + // Get available callback functions with 'pro_only' flag
1937 + $available_callbacks = $this->mxchat_get_available_callbacks();
1938 + ?>
1939 +
1940 + <div class="wrap mxchat-admin">
1941 + <!-- Add Intent Form -->
1942 + <form id="mxchat-add-intent-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
1943 + <input type="hidden" name="action" value="mxchat_add_intent">
1944 + <?php wp_nonce_field( 'mxchat_add_intent_nonce' ); ?>
1945 + <h2><?php esc_html_e( 'Add New Intent', 'mxchat' ); ?></h2>
1946 + <p class="description">
1947 + <?php esc_html_e( 'We highly encourage users to quickly read our ', 'mxchat' ); ?>
1948 + <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
1949 + <?php esc_html_e( 'documentation', 'mxchat' ); ?>
1950 + </a>
1951 + <?php esc_html_e( ' to better understand intents. Some intents require setup in the Integration tab. If your intent is not triggering lower similarity threshold test.', 'mxchat' ); ?>
1952 + </p>
1953 +
1954 + <div class="mxchat-form-group">
1955 + <label for="intent_label"><?php esc_html_e( 'Intent Label (For your reference only)', 'mxchat' ); ?></label>
1956 + <input name="intent_label" type="text" id="intent_label" class="regular-text" required
1957 + placeholder="Example Email Capture: Newsletter Signup">
1958 + </div>
1959 + <div class="mxchat-form-group">
1960 + <label for="phrases"><?php esc_html_e( 'Phrases (comma-separated)', 'mxchat' ); ?></label>
1961 + <textarea name="phrases" id="phrases" rows="5" class="large-text" required
1962 + 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>
1963 + </div>
1964 + <div class="mxchat-form-group">
1965 + <label for="callback_function"><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></label>
1966 +<select name="callback_function" id="callback_function" required>
1967 + <option value=""><?php esc_html_e('Select a Callback', 'mxchat'); ?></option>
1968 + <?php
1969 + $groups = $this->mxchat_get_available_callbacks(true); // Fetch grouped data
1970 +
1971 + foreach ($groups as $group_label => $group_callbacks) :
1972 + echo '<optgroup label="' . esc_attr($group_label) . '">';
1973 + foreach ($group_callbacks as $function => $data) :
1974 + $label = $data['label'];
1975 + $pro_only = $data['pro_only'];
1976 + $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
1977 + $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
1978 + ?>
1979 + <option value="<?php echo esc_attr($function); ?>" <?php echo $disabled; ?>>
1980 + <?php echo esc_html($label . $label_suffix); ?>
1981 + </option>
1982 + <?php endforeach;
1983 + echo '</optgroup>';
1984 + endforeach;
1985 + ?>
1986 +</select>
1987 +
1988 +
1989 +
1990 + </div>
1991 + <button type="submit" class="button button-primary submit-content-button">
1992 + <?php esc_html_e( 'Add Intent', 'mxchat' ); ?>
1993 + </button>
1994 + <div id="mxchat-intent-loading" style="display: none;"></div>
1995 + <div id="mxchat-intent-loading-text" style="display: none;">
1996 + <?php esc_html_e( 'Saving intent, please wait...', 'mxchat' ); ?>
1997 + </div>
1998 +
1999 + </form>
2000 +
2001 + <!-- Filter Form -->
2002 + <form method="get" action="">
2003 + <input type="hidden" name="page" value="mxchat-intents">
2004 + <div class="mxchat-search-group">
2005 + <input type="text" name="s" id="mxchat-intent-search" placeholder="<?php esc_attr_e( 'Search Intents', 'mxchat' ); ?>" value="<?php echo esc_attr( $search_term ); ?>">
2006 + <select name="callback_filter" id="mxchat-callback-filter">
2007 + <option value=""><?php esc_html_e( 'All Callbacks', 'mxchat' ); ?></option>
2008 + <?php foreach ( $available_callbacks as $function => $callback_data ) : ?>
2009 + <?php $label = $callback_data['label']; ?>
2010 + <option value="<?php echo esc_attr( $function ); ?>" <?php selected( $callback_filter, $function ); ?>><?php echo esc_html( $label ); ?></option>
2011 + <?php endforeach; ?>
2012 + </select>
2013 + <button type="submit" class="button"><?php esc_html_e( 'Filter', 'mxchat' ); ?></button>
2014 + </div>
2015 + </form>
2016 +
2017 + <!-- Intents Table -->
2018 + <table class="wp-list-table mxchat-intents-table widefat fixed striped">
2019 + <thead>
2020 + <tr>
2021 + <th><?php esc_html_e( 'Intent Label', 'mxchat' ); ?></th>
2022 + <th><?php esc_html_e( 'Phrases', 'mxchat' ); ?></th>
2023 + <th><?php esc_html_e( 'Callback Function', 'mxchat' ); ?></th>
2024 + <th><?php esc_html_e( 'Similarity Threshold', 'mxchat' ); ?></th>
2025 + <th><?php esc_html_e( 'Actions', 'mxchat' ); ?></th>
2026 + </tr>
2027 + </thead>
2028 + <tbody>
2029 + <?php if ( $intents ) : ?>
2030 + <?php foreach ( $intents as $intent ) : ?>
2031 + <?php
2032 + $callback_function = $intent->callback_function;
2033 + $callback_label = isset( $available_callbacks[ $callback_function ]['label'] ) ? $available_callbacks[ $callback_function ]['label'] : $callback_function;
2034 + $threshold_value = isset( $intent->similarity_threshold ) ? round( $intent->similarity_threshold * 100 ) : 85;
2035 + ?>
2036 + <tr>
2037 + <td><?php echo esc_html( $intent->intent_label ); ?></td>
2038 + <td><?php echo esc_html( $intent->phrases ); ?></td>
2039 + <td><?php echo esc_html( $callback_label ); ?></td>
2040 + <!-- Similarity Threshold Column -->
2041 + <td>
2042 + <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
2043 + <?php wp_nonce_field( 'mxchat_update_intent_threshold_nonce' ); ?>
2044 + <input type="hidden" name="action" value="mxchat_update_intent_threshold">
2045 + <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
2046 + <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 + '%'">
2047 + <output id="threshold_output_<?php echo esc_attr( $intent->id ); ?>"><?php echo esc_html( $threshold_value ); ?>%</output>
2048 + </td>
2049 + <!-- Actions Column -->
2050 + <td>
2051 + <button type="submit" class="button button-primary mxchat-save-button">
2052 + <?php esc_html_e( 'Save', 'mxchat' ); ?>
2053 + </button>
2054 + </form>
2055 +
2056 +
2057 +
2058 +
2059 + <button type="button"
2060 + class="button button-secondary mxchat-edit-button"
2061 + data-intent-id="<?php echo esc_attr($intent->id); ?>"
2062 + data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2063 + <?php esc_html_e('Edit', 'mxchat'); ?>
2064 + </button>
2065 +
2066 +
2067 +
2068 +
2069 + <!-- Delete Form -->
2070 + <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' ); ?>');">
2071 + <?php wp_nonce_field( 'mxchat_delete_intent_nonce' ); ?>
2072 + <input type="hidden" name="action" value="mxchat_delete_intent">
2073 + <input type="hidden" name="intent_id" value="<?php echo esc_attr( $intent->id ); ?>">
2074 + <button type="submit" class="button mxchat-delete-all">
2075 + <?php esc_html_e( 'Delete', 'mxchat' ); ?>
2076 + </button>
2077 + </form>
2078 + </td>
2079 + </tr>
2080 + <?php endforeach; ?>
2081 + <?php else : ?>
2082 + <tr>
2083 + <td colspan="5"><?php esc_html_e( 'No intents found.', 'mxchat' ); ?></td>
2084 + </tr>
2085 + <?php endif; ?>
2086 + </tbody>
2087 + </table>
2088 +
2089 +
2090 + <div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
2091 + <div class="mxchat-modal-content">
2092 + <span class="mxchat-modal-close">&times;</span>
2093 + <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2094 + <form id="mxchat-edit-intent-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2095 + <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2096 + <input type="hidden" name="action" value="mxchat_edit_intent">
2097 + <input type="hidden" name="intent_id" id="edit_intent_id">
2098 + <div class="mxchat-form-group">
2099 + <label for="edit_phrases"><?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?></label>
2100 + <textarea name="phrases" id="edit_phrases" rows="5" class="large-text" required></textarea>
2101 + </div>
2102 + <button type="submit" class="button button-primary">
2103 + <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2104 + </button>
2105 + </form>
2106 + </div>
2107 +</div>
2108 +
2109 +
2110 +
2111 +
2112 + </div>
2113 + <?php
2114 +}
2115 +
2116 +
2117 +
2118 +/**
2119 + * Handle editing of intent phrases
2120 + *
2121 + * @since 1.0.0
2122 + * @return void
2123 + */
2124 +public function handle_edit_intent() {
2125 + // Verify nonce and user capabilities
2126 + if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
2127 + wp_die(esc_html__('Security check failed.', 'mxchat'));
2128 + }
2129 +
2130 + if (!current_user_can('manage_options')) {
2131 + wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
2132 + }
2133 +
2134 + // Validate and sanitize input
2135 + $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2136 + if (!$intent_id) {
2137 + wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
2138 + }
2139 +
2140 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2141 + if (empty($phrases_input)) {
2142 + wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
2143 + }
2144 +
2145 + // Process phrases the same way as in intent creation
2146 + $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2147 +
2148 + if (empty($phrases_array)) {
2149 + wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
2150 + }
2151 +
2152 + // Generate embeddings and combine them
2153 + $vectors = [];
2154 + foreach ($phrases_array as $phrase) {
2155 + $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2156 + if (is_array($embedding_vector)) {
2157 + $vectors[] = $embedding_vector;
2158 + } else {
2159 + wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
2160 + }
2161 + }
2162 +
2163 + if (empty($vectors)) {
2164 + wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
2165 + }
2166 +
2167 + // Create combined vector just like in intent creation
2168 + $combined_vector = $this->mxchat_average_vectors($vectors);
2169 + $serialized_vector = maybe_serialize($combined_vector);
2170 +
2171 + global $wpdb;
2172 + $table_name = $wpdb->prefix . 'mxchat_intents';
2173 +
2174 + // Update the intent with both new phrases and the combined vector
2175 + $result = $wpdb->update(
2176 + $table_name,
2177 + array(
2178 + 'phrases' => implode(', ', $phrases_array),
2179 + 'embedding_vector' => $serialized_vector
2180 + ),
2181 + array('id' => $intent_id),
2182 + array('%s', '%s'),
2183 + array('%d')
2184 + );
2185 +
2186 + if (false === $result) {
2187 + wp_die(esc_html__('Failed to update intent.', 'mxchat'));
2188 + }
2189 +
2190 + // Redirect back to the intents page with a success message
2191 + $redirect_url = add_query_arg(
2192 + array(
2193 + 'page' => 'mxchat-intents',
2194 + 'updated' => 'true'
2195 + ),
2196 + admin_url('admin.php')
2197 + );
2198 +
2199 + wp_safe_redirect($redirect_url);
2200 + exit;
2201 +}
2202 +public function mxchat_handle_update_intent_threshold() {
2203 + if ( ! current_user_can( 'manage_options' ) ) {
2204 + wp_die( esc_html__('Unauthorized user', 'mxchat') );
2205 + }
2206 +
2207 + check_admin_referer('mxchat_update_intent_threshold_nonce');
2208 +
2209 + if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
2210 + global $wpdb;
2211 + $table_name = $wpdb->prefix . 'mxchat_intents';
2212 + $intent_id = intval($_POST['intent_id']);
2213 + $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
2214 + $similarity_threshold = $threshold_percentage / 100;
2215 +
2216 + $wpdb->update(
2217 + $table_name,
2218 + ['similarity_threshold' => $similarity_threshold],
2219 + ['id' => $intent_id],
2220 + ['%f'],
2221 + ['%d']
2222 + );
2223 + }
2224 +
2225 + wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2226 + exit;
2227 +}
2228 +
2229 +public function mxchat_handle_add_intent() {
2230 + if ( ! current_user_can( 'manage_options' ) ) {
2231 + wp_die( esc_html__('Unauthorized user', 'mxchat') );
2232 + }
2233 +
2234 + check_admin_referer('mxchat_add_intent_nonce');
2235 +
2236 + global $wpdb;
2237 + $table_name = $wpdb->prefix . 'mxchat_intents';
2238 +
2239 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
2240 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2241 + $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2242 + $default_threshold = 0.85;
2243 +
2244 + if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
2245 + wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
2246 + }
2247 +
2248 + $available_callbacks = $this->mxchat_get_available_callbacks();
2249 +
2250 + if (!array_key_exists($callback_function, $available_callbacks)) {
2251 + wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
2252 + }
2253 +
2254 + $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2255 + if ($is_pro_only && !$this->is_activated) {
2256 + wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
2257 + }
2258 +
2259 + $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2260 +
2261 + if (empty($phrases_array)) {
2262 + wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
2263 + }
2264 +
2265 + $vectors = [];
2266 + foreach ($phrases_array as $phrase) {
2267 + $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2268 + if (is_array($embedding_vector)) {
2269 + $vectors[] = $embedding_vector;
2270 + } else {
2271 + wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
2272 + }
2273 + }
2274 +
2275 + if (!empty($vectors)) {
2276 + $combined_vector = $this->mxchat_average_vectors($vectors);
2277 + $serialized_vector = maybe_serialize($combined_vector);
2278 +
2279 + $result = $wpdb->insert($table_name, [
2280 + 'intent_label' => $intent_label,
2281 + 'phrases' => implode(', ', $phrases_array),
2282 + 'embedding_vector' => $serialized_vector,
2283 + 'callback_function' => $callback_function,
2284 + 'similarity_threshold' => $default_threshold,
2285 + ]);
2286 +
2287 + if ($result === false) {
2288 + wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
2289 + }
2290 + } else {
2291 + wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
2292 + }
2293 +
2294 + wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2295 + exit;
2296 +}
2297 +
2298 +
2299 +
2300 +
2301 +
2302 +
2303 +private function mxchat_get_available_callbacks($grouped = false) {
2304 + $callbacks = [
2305 + 'mxchat_handle_email_capture' => [
2306 + 'label' => 'Email Capture',
2307 + 'pro_only' => false,
2308 + 'group' => 'Customer Engagement',
2309 + ],
2310 + 'mxchat_handle_product_inquiry' => [
2311 + 'label' => 'Show Product Card',
2312 + 'pro_only' => true,
2313 + 'group' => 'WooCommerce Features',
2314 + ],
2315 + 'mxchat_handle_order_history' => [
2316 + 'label' => 'Order History',
2317 + 'pro_only' => true,
2318 + 'group' => 'WooCommerce Features',
2319 + ],
2320 + 'mxchat_generate_image' => [
2321 + 'label' => 'Generate Image',
2322 + 'pro_only' => true,
2323 + 'group' => 'Other Features',
2324 + ],
2325 + 'mxchat_handle_search_request' => [
2326 + 'label' => 'Brave Web Search',
2327 + 'pro_only' => false,
2328 + 'group' => 'Search Features',
2329 + ],
2330 + 'mxchat_handle_image_search_request' => [
2331 + 'label' => 'Brave Image Search',
2332 + 'pro_only' => false,
2333 + 'group' => 'Search Features',
2334 + ],
2335 + 'mxchat_handle_add_to_cart_intent' => [
2336 + 'label' => 'Add to Cart',
2337 + 'pro_only' => true,
2338 + 'group' => 'WooCommerce Features',
2339 + ],
2340 + 'mxchat_handle_checkout_intent' => [
2341 + 'label' => 'Proceed to Checkout',
2342 + 'pro_only' => true,
2343 + 'group' => 'WooCommerce Features',
2344 + ],
2345 + 'mxchat_handle_pdf_discussion' => [
2346 + 'label' => 'Chat with PDF',
2347 + 'pro_only' => true,
2348 + 'group' => 'Other Features',
2349 + ],
2350 + 'mxchat_handle_product_recommendations' => [
2351 + 'label' => 'Product Recommendations',
2352 + 'pro_only' => true,
2353 + 'group' => 'WooCommerce Features',
2354 + ],
2355 + 'mxchat_live_agent_handover' => [
2356 + 'label' => 'Live Agent',
2357 + 'pro_only' => true,
2358 + 'group' => 'Customer Engagement',
2359 + ],
2360 + 'mxchat_handle_switch_to_chatbot_intent' => [
2361 + 'label' => 'Back to Chatbot',
2362 + 'pro_only' => true,
2363 + 'group' => 'Customer Engagement',
2364 + ],
2365 + ];
2366 +
2367 + // Return grouped structure if requested
2368 + if ($grouped) {
2369 + $grouped_callbacks = [];
2370 + foreach ($callbacks as $key => $data) {
2371 + $group_label = $data['group'] ?? 'Other Features';
2372 + $grouped_callbacks[$group_label][$key] = [
2373 + 'label' => $data['label'],
2374 + 'pro_only' => $data['pro_only'],
2375 + ];
2376 + }
2377 + return $grouped_callbacks;
2378 + }
2379 +
2380 + return $callbacks;
2381 +}
2382 +
2383 +
2384 +
2385 +private function mxchat_average_vectors($vectors) {
2386 + $vector_length = count($vectors[0]);
2387 + $sum_vector = array_fill(0, $vector_length, 0);
2388 +
2389 + foreach ($vectors as $vector) {
2390 + for ($i = 0; $i < $vector_length; $i++) {
2391 + $sum_vector[$i] += $vector[$i];
2392 + }
2393 + }
2394 +
2395 + // Divide each component by the number of vectors to get the average
2396 + $num_vectors = count($vectors);
2397 + for ($i = 0; $i < $vector_length; $i++) {
2398 + $sum_vector[$i] /= $num_vectors;
2399 + }
2400 +
2401 + return $sum_vector;
2402 +}
2403 +
2404 +public function mxchat_handle_delete_intent() {
2405 + if ( ! current_user_can( 'manage_options' ) ) {
2406 + wp_die( esc_html__('Unauthorized user', 'mxchat') );
2407 + }
2408 +
2409 + check_admin_referer('mxchat_delete_intent_nonce');
2410 +
2411 + if (isset($_POST['intent_id'])) {
2412 + global $wpdb;
2413 + $table_name = $wpdb->prefix . 'mxchat_intents';
2414 + $intent_id = intval($_POST['intent_id']);
2415 +
2416 + $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
2417 + }
2418 +
2419 + wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2420 + exit;
2421 +}
2422 +
2423 +
2424 +public function mxchat_page_init() {
2425 + // Register settings
2426 + register_setting(
2427 + 'mxchat_option_group',
2428 + 'mxchat_options',
2429 + array($this, 'mxchat_sanitize')
2430 + );
2431 +
2432 + register_setting(
2433 + 'mxchat_option_group',
2434 + 'mxchat_similarity_threshold',
2435 + array(
2436 + 'type' => 'number',
2437 + 'sanitize_callback' => function($value) {
2438 + $value = absint($value); // Ensure it's an integer
2439 + return min(max($value, 70), 85); // Enforce range
2440 + },
2441 + 'default' => 80,
2442 + )
2443 + );
2444 +
2445 +
2446 + // Chatbot Settings Section
2447 + add_settings_section(
2448 + 'mxchat_chatbot_section',
2449 + 'Chatbot Settings',
2450 + null,
2451 + 'mxchat-chatbot'
2452 + );
2453 +
2454 + // Similarity Threshold Slider
2455 + add_settings_field(
2456 + 'similarity_threshold', // Field ID
2457 + 'Similarity Threshold', // Field title
2458 + array($this, 'mxchat_similarity_threshold_callback'), // Callback function
2459 + 'mxchat-chatbot', // Page
2460 + 'mxchat_chatbot_section' // Section
2461 + );
2462 +
2463 + // Existing fields...
2464 + add_settings_field(
2465 + 'api_key',
2466 + 'OpenAI API Key',
2467 + array($this, 'api_key_callback'),
2468 + 'mxchat-chatbot',
2469 + 'mxchat_chatbot_section'
2470 + );
2471 +
2472 + add_settings_field(
2473 + 'xai_api_key',
2474 + 'X.AI API Key',
2475 + array($this, 'xai_api_key_callback'),
2476 + 'mxchat-chatbot',
2477 + 'mxchat_chatbot_section'
2478 + );
2479 +
2480 + add_settings_field(
2481 + 'claude_api_key',
2482 + 'Claude API Key',
2483 + array($this, 'claude_api_key_callback'),
2484 + 'mxchat-chatbot',
2485 + 'mxchat_chatbot_section'
2486 + );
2487 +
2488 +
2489 + add_settings_field(
2490 + 'system_prompt_instructions',
2491 + 'AI Instructions (Behavior)',
2492 + array($this, 'system_prompt_instructions_callback'),
2493 + 'mxchat-chatbot',
2494 + 'mxchat_chatbot_section'
2495 + );
2496 +
2497 + add_settings_field(
2498 + 'model',
2499 + 'Model',
2500 + array($this, 'mxchat_model_callback'),
2501 + 'mxchat-chatbot',
2502 + 'mxchat_chatbot_section'
2503 + );
2504 +
2505 + add_settings_field(
2506 + 'top_bar_title',
2507 + 'Top Bar Title',
2508 + array($this, 'mxchat_top_bar_title_callback'),
2509 + 'mxchat-chatbot',
2510 + 'mxchat_chatbot_section'
2511 + );
2512 +
2513 + add_settings_field(
2514 + 'enable_email_block',
2515 + 'Require Email Before Chat',
2516 + array($this, 'enable_email_block_callback'),
2517 + 'mxchat-chatbot',
2518 + 'mxchat_chatbot_section'
2519 + );
2520 +
2521 + add_settings_field(
2522 + 'email_blocker_header_content',
2523 + 'Require Email Chat Content',
2524 + array($this, 'email_blocker_header_content_callback'),
2525 + 'mxchat-chatbot',
2526 + 'mxchat_chatbot_section'
2527 + );
2528 +
2529 + add_settings_field(
2530 + 'email_blocker_button_text',
2531 + 'Require Email Chat Button Text',
2532 + [$this, 'email_blocker_button_text_callback'],
2533 + 'mxchat-chatbot',
2534 + 'mxchat_chatbot_section'
2535 + );
2536 +
2537 + add_settings_field(
2538 + 'intro_message',
2539 + 'Introductory Message',
2540 + array($this, 'mxchat_intro_message_callback'),
2541 + 'mxchat-chatbot',
2542 + 'mxchat_chatbot_section'
2543 + );
2544 +
2545 + add_settings_field(
2546 + 'input_copy',
2547 + 'Input Copy',
2548 + array($this, 'mxchat_input_copy_callback'),
2549 + 'mxchat-chatbot',
2550 + 'mxchat_chatbot_section'
2551 + );
2552 +
2553 + add_settings_field(
2554 + 'rate_limit_logged_in',
2555 + __('Rate Limit for Logged-in Users', 'mxchat'),
2556 + array($this, 'mxchat_rate_limit_logged_in_callback'),
2557 + 'mxchat-chatbot',
2558 + 'mxchat_chatbot_section'
2559 + );
2560 +
2561 + add_settings_field(
2562 + 'rate_limit_logged_out',
2563 + __('Rate Limit for Logged-out Users', 'mxchat'),
2564 + array($this, 'mxchat_rate_limit_logged_out_callback'),
2565 + 'mxchat-chatbot',
2566 + 'mxchat_chatbot_section'
2567 + );
2568 +
2569 + add_settings_field(
2570 + 'rate_limit_message',
2571 + 'Rate Limit Message',
2572 + array($this, 'mxchat_rate_limit_message_callback'),
2573 + 'mxchat-chatbot',
2574 + 'mxchat_chatbot_section'
2575 + );
2576 +
2577 + add_settings_field(
2578 + 'pre_chat_message',
2579 + 'Chat Teaser Pop-up',
2580 + array($this, 'mxchat_pre_chat_message_callback'),
2581 + 'mxchat-chatbot',
2582 + 'mxchat_chatbot_section'
2583 + );
2584 +
2585 + add_settings_field(
2586 + 'append_to_body',
2587 + 'Append Chat Widget to Body',
2588 + array($this, 'mxchat_append_to_body_callback'),
2589 + 'mxchat-chatbot',
2590 + 'mxchat_chatbot_section'
2591 + );
2592 +
2593 + add_settings_field(
2594 + 'privacy_toggle',
2595 + 'Toggle Privacy Notice',
2596 + array($this, 'mxchat_privacy_toggle_callback'),
2597 + 'mxchat-chatbot',
2598 + 'mxchat_chatbot_section'
2599 + );
2600 +
2601 + add_settings_field(
2602 + 'complianz_toggle',
2603 + 'Enable Complianz',
2604 + array($this, 'mxchat_complianz_toggle_callback'),
2605 + 'mxchat-chatbot',
2606 + 'mxchat_chatbot_section'
2607 + );
2608 +
2609 + add_settings_field(
2610 + 'link_target_toggle',
2611 + 'Open Links in a New Tab',
2612 + array($this, 'mxchat_link_target_toggle_callback'),
2613 + 'mxchat-chatbot',
2614 + 'mxchat_chatbot_section'
2615 + );
2616 +
2617 + add_settings_field(
2618 + 'chat_persistence_toggle',
2619 + 'Enable Chat Persistence',
2620 + array($this, 'mxchat_chat_persistence_toggle_callback'),
2621 + 'mxchat-chatbot',
2622 + 'mxchat_chatbot_section'
2623 + );
2624 +
2625 + add_settings_field(
2626 + 'popular_question_1',
2627 + 'Popular Question 1',
2628 + array($this, 'mxchat_popular_question_1_callback'),
2629 + 'mxchat-chatbot',
2630 + 'mxchat_chatbot_section'
2631 +);
2632 +
2633 +add_settings_field(
2634 + 'popular_question_2',
2635 + 'Popular Question 2',
2636 + array($this, 'mxchat_popular_question_2_callback'),
2637 + 'mxchat-chatbot',
2638 + 'mxchat_chatbot_section'
2639 +);
2640 +
2641 +add_settings_field(
2642 + 'popular_question_3',
2643 + 'Popular Question 3',
2644 + array($this, 'mxchat_popular_question_3_callback'),
2645 + 'mxchat-chatbot',
2646 + 'mxchat_chatbot_section'
2647 +);
2648 +
2649 +add_settings_field(
2650 + 'additional_popular_questions',
2651 + 'Additional Popular Questions',
2652 + array($this, 'mxchat_additional_popular_questions_callback'),
2653 + 'mxchat-chatbot',
2654 + 'mxchat_chatbot_section'
2655 +);
2656 +
2657 +
2658 +// WooCommerce Settings Section
2659 +add_settings_section(
2660 + 'mxchat_woocommerce_section',
2661 + 'WooCommerce Settings',
2662 + null,
2663 + 'mxchat-embed'
2664 +);
2665 +
2666 +// Loops Settings Section
2667 +add_settings_section(
2668 + 'mxchat_loops_section',
2669 + 'Loops Settings',
2670 + null,
2671 + 'mxchat-embed'
2672 +);
2673 +
2674 +// WooCommerce Settings Fields
2675 +add_settings_field(
2676 + 'enable_woocommerce_integration',
2677 + 'Automatically Embed Products',
2678 + array($this, 'mxchat_enable_woocommerce_integration_callback'),
2679 + 'mxchat-embed',
2680 + 'mxchat_woocommerce_section'
2681 +);
2682 +
2683 +
2684 +add_settings_field(
2685 + 'woocommerce_consumer_key',
2686 + 'WooCommerce Consumer Key',
2687 + array($this, 'mxchat_woocommerce_consumer_key_callback'),
2688 + 'mxchat-embed',
2689 + 'mxchat_woocommerce_section'
2690 +);
2691 +
2692 +add_settings_field(
2693 + 'woocommerce_consumer_secret',
2694 + 'WooCommerce Consumer Secret',
2695 + array($this, 'mxchat_woocommerce_consumer_secret_callback'),
2696 + 'mxchat-embed',
2697 + 'mxchat_woocommerce_section'
2698 +);
2699 +
2700 +// Loops Settings Fields
2701 +add_settings_field(
2702 + 'loops_api_key',
2703 + 'Loops API Key',
2704 + array($this, 'mxchat_loops_api_key_callback'),
2705 + 'mxchat-embed',
2706 + 'mxchat_loops_section'
2707 +);
2708 +
2709 +add_settings_field(
2710 + 'loops_mailing_list',
2711 + 'Loops Mailing List',
2712 + array($this, 'mxchat_loops_mailing_list_callback'),
2713 + 'mxchat-embed',
2714 + 'mxchat_loops_section'
2715 +);
2716 +
2717 +add_settings_field(
2718 + 'triggered_phrase_response',
2719 + 'Triggered Phrase Response',
2720 + array($this, 'mxchat_triggered_phrase_response_callback'),
2721 + 'mxchat-embed',
2722 + 'mxchat_loops_section'
2723 +);
2724 +
2725 +add_settings_field(
2726 + 'email_capture_response',
2727 + 'Email Capture Response',
2728 + array($this, 'mxchat_email_capture_response_callback'),
2729 + 'mxchat-embed',
2730 + 'mxchat_loops_section'
2731 +);
2732 +
2733 +
2734 + // Brave Search Settings Fields
2735 + add_settings_section(
2736 + 'mxchat_brave_section',
2737 + __('Brave Search Settings', 'mxchat'),
2738 + array($this, 'mxchat_brave_section_callback'),
2739 + 'mxchat-embed'
2740 + );
2741 +
2742 + add_settings_field(
2743 + 'brave_api_key',
2744 + __('Brave API Key', 'mxchat'),
2745 + array($this, 'mxchat_brave_api_key_callback'),
2746 + 'mxchat-embed',
2747 + 'mxchat_brave_section'
2748 + );
2749 +
2750 + add_settings_field(
2751 + 'brave_image_count',
2752 + __('Number of Images to Return', 'mxchat'),
2753 + array($this, 'mxchat_brave_image_count_callback'),
2754 + 'mxchat-embed',
2755 + 'mxchat_brave_section'
2756 + );
2757 +
2758 + add_settings_field(
2759 + 'brave_safe_search',
2760 + __('Safe Search', 'mxchat'),
2761 + array($this, 'mxchat_brave_safe_search_callback'),
2762 + 'mxchat-embed',
2763 + 'mxchat_brave_section'
2764 + );
2765 +
2766 + add_settings_field(
2767 + 'brave_news_count',
2768 + __('Number of News Articles', 'mxchat'),
2769 + array($this, 'mxchat_brave_news_count_callback'),
2770 + 'mxchat-embed',
2771 + 'mxchat_brave_section'
2772 + );
2773 +
2774 + add_settings_field(
2775 + 'brave_country',
2776 + __('Country', 'mxchat'),
2777 + array($this, 'mxchat_brave_country_callback'),
2778 + 'mxchat-embed',
2779 + 'mxchat_brave_section'
2780 + );
2781 +
2782 + add_settings_field(
2783 + 'brave_language',
2784 + __('Language', 'mxchat'),
2785 + array($this, 'mxchat_brave_language_callback'),
2786 + 'mxchat-embed',
2787 + 'mxchat_brave_section'
2788 + );
2789 +
2790 +
2791 + // Chat with PDF Intent Settings Fields
2792 +add_settings_section(
2793 + 'mxchat_pdf_intent_section',
2794 + __('Toolbar Settings & Intents', 'mxchat'),
2795 + array($this, 'mxchat_pdf_intent_section_callback'),
2796 + 'mxchat-embed'
2797 +);
2798 +
2799 +add_settings_field(
2800 + 'chat_toolbar_toggle',
2801 + __('Show Chat Toolbar', 'mxchat'),
2802 + array($this, 'mxchat_chat_toolbar_toggle_callback'),
2803 + 'mxchat-embed',
2804 + 'mxchat_pdf_intent_section'
2805 +);
2806 +
2807 +
2808 +add_settings_field(
2809 + 'pdf_intent_trigger_text',
2810 + __('Intent Trigger Text', 'mxchat'),
2811 + array($this, 'mxchat_pdf_intent_trigger_text_callback'),
2812 + 'mxchat-embed',
2813 + 'mxchat_pdf_intent_section'
2814 +);
2815 +
2816 +add_settings_field(
2817 + 'pdf_intent_success_text',
2818 + __('Success Text', 'mxchat'),
2819 + array($this, 'mxchat_pdf_intent_success_text_callback'),
2820 + 'mxchat-embed',
2821 + 'mxchat_pdf_intent_section'
2822 +);
2823 +
2824 +add_settings_field(
2825 + 'pdf_intent_error_text',
2826 + __('Error Text', 'mxchat'),
2827 + array($this, 'mxchat_pdf_intent_error_text_callback'),
2828 + 'mxchat-embed',
2829 + 'mxchat_pdf_intent_section'
2830 +);
2831 +
2832 +// Add PDF Maximum Pages Field
2833 +add_settings_field(
2834 + 'pdf_max_pages',
2835 + __('Maximum Document Pages', 'mxchat'),
2836 + array($this, 'mxchat_pdf_max_pages_callback'),
2837 + 'mxchat-embed',
2838 + 'mxchat_pdf_intent_section'
2839 +);
2840 +
2841 +
2842 +
2843 +
2844 +// Live Agent Settings Fields
2845 +add_settings_section(
2846 + 'mxchat_live_agent_section',
2847 + __('Live Agent Settings', 'mxchat'),
2848 + array($this, 'mxchat_live_agent_section_callback'),
2849 + 'mxchat-embed'
2850 +);
2851 +
2852 +// Live Agent Status Fields (add at top of live agent settings)
2853 +add_settings_field(
2854 + 'live_agent_status',
2855 + __('Live Agent Status', 'mxchat'),
2856 + array($this, 'mxchat_live_agent_status_callback'),
2857 + 'mxchat-embed',
2858 + 'mxchat_live_agent_section'
2859 +);
2860 +
2861 +add_settings_field(
2862 + 'live_agent_notification_message',
2863 + __('Notification Message', 'mxchat'),
2864 + array($this, 'mxchat_live_agent_notification_message_callback'),
2865 + 'mxchat-embed',
2866 + 'mxchat_live_agent_section'
2867 +);
2868 +
2869 +add_settings_field(
2870 + 'live_agent_away_message',
2871 + __('Away Message', 'mxchat'),
2872 + array($this, 'mxchat_live_agent_away_message_callback'),
2873 + 'mxchat-embed',
2874 + 'mxchat_live_agent_section'
2875 +);
2876 +
2877 +add_settings_field(
2878 + 'live_agent_webhook_url',
2879 + __('Slack Webhook URL', 'mxchat'),
2880 + array($this, 'mxchat_live_agent_webhook_url_callback'),
2881 + 'mxchat-embed',
2882 + 'mxchat_live_agent_section'
2883 +);
2884 +
2885 +add_settings_field(
2886 + 'live_agent_secret_key',
2887 + __('Slack Secret Key', 'mxchat'),
2888 + array($this, 'mxchat_live_agent_secret_key_callback'),
2889 + 'mxchat-embed',
2890 + 'mxchat_live_agent_section'
2891 +);
2892 +
2893 +// Live Agent Integration Fields
2894 +add_settings_field(
2895 + 'live_agent_bot_token',
2896 + __('Slack Bot OAuth Token', 'mxchat'),
2897 + array($this, 'mxchat_live_agent_bot_token_callback'),
2898 + 'mxchat-embed',
2899 + 'mxchat_live_agent_section'
2900 +);
2901 +
2902 +
2903 + // Theme Settings Section
2904 + add_settings_section(
2905 + 'mxchat_theme_section',
2906 + 'Theme Settings',
2907 + null,
2908 + 'mxchat-theme'
2909 + );
2910 +
2911 + add_settings_field(
2912 + 'close_button_color',
2913 + 'Close Button & Title Color',
2914 + array($this, 'mxchat_close_button_color_callback'),
2915 + 'mxchat-theme',
2916 + 'mxchat_theme_section'
2917 + );
2918 +
2919 + add_settings_field(
2920 + 'chatbot_bg_color',
2921 + 'Chatbot Background Color',
2922 + array($this, 'mxchat_chatbot_bg_color_callback'),
2923 + 'mxchat-theme',
2924 + 'mxchat_theme_section'
2925 + );
2926 +
2927 + add_settings_field(
2928 + 'user_message_bg_color',
2929 + 'User Message Background Color',
2930 + array($this, 'mxchat_user_message_bg_color_callback'),
2931 + 'mxchat-theme',
2932 + 'mxchat_theme_section'
2933 + );
2934 +
2935 + add_settings_field(
2936 + 'user_message_font_color',
2937 + 'User Message Font Color',
2938 + array($this, 'mxchat_user_message_font_color_callback'),
2939 + 'mxchat-theme',
2940 + 'mxchat_theme_section'
2941 + );
2942 +
2943 + add_settings_field(
2944 + 'bot_message_bg_color',
2945 + 'Bot Message Background Color',
2946 + array($this, 'mxchat_bot_message_bg_color_callback'),
2947 + 'mxchat-theme',
2948 + 'mxchat_theme_section'
2949 + );
2950 +
2951 + add_settings_field(
2952 + 'bot_message_font_color',
2953 + 'Bot Message Font Color',
2954 + array($this, 'mxchat_bot_message_font_color_callback'),
2955 + 'mxchat-theme',
2956 + 'mxchat_theme_section'
2957 + );
2958 +
2959 + add_settings_field(
2960 + 'top_bar_bg_color',
2961 + 'Top Bar Background Color',
2962 + array($this, 'mxchat_top_bar_bg_color_callback'),
2963 + 'mxchat-theme',
2964 + 'mxchat_theme_section'
2965 + );
2966 +
2967 + add_settings_field(
2968 + 'send_button_font_color',
2969 + 'Send Button Color',
2970 + array($this, 'mxchat_send_button_font_color_callback'),
2971 + 'mxchat-theme',
2972 + 'mxchat_theme_section'
2973 + );
2974 +
2975 + add_settings_field(
2976 + 'chat_input_font_color',
2977 + 'Chat Input Font Color',
2978 + array($this, 'mxchat_chat_input_font_color_callback'),
2979 + 'mxchat-theme',
2980 + 'mxchat_theme_section'
2981 + );
2982 +
2983 + add_settings_field(
2984 + 'chatbot_background_color',
2985 + 'Floating Widget Background Color',
2986 + array($this, 'mxchat_chatbot_background_color_callback'),
2987 + 'mxchat-theme',
2988 + 'mxchat_theme_section'
2989 + );
2990 +
2991 + add_settings_field(
2992 + 'icon_color',
2993 + 'Chatbot Icon Color',
2994 + array($this, 'mxchat_icon_color_callback'),
2995 + 'mxchat-theme',
2996 + 'mxchat_theme_section'
2997 + );
2998 +
2999 + add_settings_field(
3000 + 'custom_icon',
3001 + 'Custom Chatbot Icon (PNG)',
3002 + array($this, 'mxchat_custom_icon_callback'),
3003 + 'mxchat-theme',
3004 + 'mxchat_theme_section'
3005 + );
3006 +
3007 + add_settings_field(
3008 + 'title_icon',
3009 + 'Title Bar Icon (PNG)',
3010 + array($this, 'mxchat_title_icon_callback'),
3011 + 'mxchat-theme',
3012 + 'mxchat_theme_section'
3013 + );
3014 +
3015 +
3016 + add_settings_field(
3017 + 'live_agent_message_bg_color',
3018 + 'Live Agent Background Color',
3019 + array($this, 'mxchat_live_agent_message_bg_color_callback'),
3020 + 'mxchat-theme',
3021 + 'mxchat_theme_section'
3022 + );
3023 +
3024 + add_settings_field(
3025 + 'live_agent_message_font_color',
3026 + 'Live Agent Font Color',
3027 + array($this, 'mxchat_live_agent_message_font_color_callback'),
3028 + 'mxchat-theme',
3029 + 'mxchat_theme_section'
3030 + );
3031 +
3032 + // Mode Indicator Background Color
3033 + add_settings_field(
3034 + 'mode_indicator_bg_color',
3035 + 'Mode Indicator Background Color',
3036 + array($this, 'mxchat_mode_indicator_bg_color_callback'),
3037 + 'mxchat-theme',
3038 + 'mxchat_theme_section'
3039 + );
3040 +
3041 + // Mode Indicator Font Color
3042 + add_settings_field(
3043 + 'mode_indicator_font_color',
3044 + 'Mode Indicator Font Color',
3045 + array($this, 'mxchat_mode_indicator_font_color_callback'),
3046 + 'mxchat-theme',
3047 + 'mxchat_theme_section'
3048 + );
3049 +
3050 + add_settings_field(
3051 + 'toolbar_icon_color',
3052 + 'Toolbar Icon Color',
3053 + array($this, 'mxchat_toolbar_icon_color_callback'),
3054 + 'mxchat-theme',
3055 + 'mxchat_theme_section'
3056 + );
3057 +
3058 + // General Settings Section
3059 + add_settings_section(
3060 + 'mxchat_general_section',
3061 + 'Frequently Asked Questions (FAQ)',
3062 + null,
3063 + 'mxchat-general'
3064 + );
3065 +
3066 +
3067 +
3068 +
3069 +
3070 +
3071 +}
3072 +
3073 +public function mxchat_prompts_page_init() {
3074 + // Register settings for the prompts/knowledge base page
3075 + register_setting(
3076 + 'mxchat_prompts_options',
3077 + 'mxchat_auto_sync_posts',
3078 + array(
3079 + 'type' => 'boolean',
3080 + 'description' => 'Automatically sync posts to knowledge base',
3081 + 'sanitize_callback' => array($this, 'sanitize_sync_setting'),
3082 + 'default' => 0,
3083 + 'show_in_rest' => false,
3084 + )
3085 + );
3086 + register_setting(
3087 + 'mxchat_prompts_options',
3088 + 'mxchat_auto_sync_pages',
3089 + array(
3090 + 'type' => 'boolean',
3091 + 'description' => 'Automatically sync pages to knowledge base',
3092 + 'sanitize_callback' => array($this, 'sanitize_sync_setting'),
3093 + 'default' => 0,
3094 + 'show_in_rest' => false,
3095 + )
3096 + );
3097 +
3098 + // Add settings saved message
3099 + add_action('admin_notices', array($this, 'sync_settings_notice'));
3100 +}
3101 +
3102 +public function sync_settings_notice() {
3103 + // Only show notice on our plugin page
3104 + if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3105 + return;
3106 + }
3107 +
3108 + // Check if settings were updated
3109 + if (isset($_GET['settings-updated'])) {
3110 + ?>
3111 + <div class="notice notice-success is-dismissible">
3112 + <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
3113 + </div>
3114 + <?php
3115 + }
3116 +}
3117 +// Add this sanitization function to your class
3118 +public function sanitize_sync_setting($input) {
3119 + return (bool)$input ? '1' : '';
3120 +}
3121 +
3122 +
3123 +
3124 +public function mxchat_handle_activate_license() {
3125 + // Check nonce
3126 + if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3127 + wp_send_json_error('Invalid security token');
3128 + return;
3129 + }
3130 +
3131 + // Verify user capabilities
3132 + if (!current_user_can('manage_options')) {
3133 + wp_send_json_error('Unauthorized access');
3134 + return;
3135 + }
3136 +
3137 + $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3138 + $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
3139 +
3140 + if (empty($license_key) || empty($customer_email)) {
3141 + wp_send_json_error('Email or License Key is missing');
3142 + return;
3143 + }
3144 +
3145 + $product_id = 'MxChatPRO';
3146 + $response = wp_remote_get(
3147 + add_query_arg(
3148 + array(
3149 + 'wc-api' => 'software-api',
3150 + 'request' => 'activation',
3151 + 'email' => $customer_email,
3152 + 'license_key' => $license_key,
3153 + 'product_id' => $product_id
3154 + ),
3155 + 'http://mxchat.ai/'
3156 + )
3157 + );
3158 +
3159 + if (is_wp_error($response)) {
3160 + wp_send_json_error('Activation failed due to a server error: ' . $response->get_error_message());
3161 + return;
3162 + }
3163 +
3164 + $body = wp_remote_retrieve_body($response);
3165 + $data = json_decode($body);
3166 +
3167 + if ($data && isset($data->activated) && $data->activated) {
3168 + update_option('mxchat_license_status', 'active');
3169 + update_option('mxchat_pro_email', $customer_email);
3170 + update_option('mxchat_activation_key', $license_key);
3171 + wp_send_json_success(array('message' => 'License activated successfully'));
3172 + } else {
3173 + $error_message = isset($data->error) ? $data->error : 'Activation failed';
3174 + update_option('mxchat_license_status', 'inactive');
3175 + update_option('mxchat_license_error', $error_message);
3176 + wp_send_json_error($error_message);
3177 + }
3178 +}
3179 +
3180 +public function mxchat_rate_limit_logged_in_callback() {
3181 + // Load the entire 'mxchat_options' array
3182 + $all_options = get_option('mxchat_options', []);
3183 +
3184 + // Retrieve the saved rate limit or use the default value
3185 + $default_rate_limit = '100';
3186 + $selected_rate_limit = isset($all_options['rate_limit_logged_in']) ? $all_options['rate_limit_logged_in'] : $default_rate_limit;
3187 +
3188 + // Define available rate limits
3189 + $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3190 +
3191 + // Output the dropdown
3192 + echo '<div class="pro-feature-wrapper active">';
3193 + echo '<select id="rate_limit_logged_in" name="rate_limit_logged_in">';
3194 + foreach ($rate_limits as $limit) {
3195 + echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3196 + }
3197 + echo '</select>';
3198 + echo '<p class="description">Set the maximum number of messages a logged-in user can send within a 24-hour period.</p>';
3199 + echo '</div>';
3200 +}
3201 +
3202 +
3203 +
3204 +public function mxchat_rate_limit_logged_out_callback() {
3205 + // Load the entire 'mxchat_options' array
3206 + $all_options = get_option('mxchat_options', []);
3207 +
3208 + // Retrieve the saved rate limit or use the default value
3209 + $default_rate_limit = '10';
3210 + $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
3211 +
3212 + // Define available rate limits
3213 + $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3214 +
3215 + // Output the dropdown
3216 + echo '<div class="pro-feature-wrapper active">';
3217 + echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3218 + foreach ($rate_limits as $limit) {
3219 + echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3220 + }
3221 + echo '</select>';
3222 + echo '<p class="description">Set the maximum number of messages a logged-out user can send within a 24-hour period.</p>';
3223 + echo '</div>';
3224 +}
3225 +
3226 +public function mxchat_rate_limit_message_callback() {
3227 + // Load the entire 'mxchat_options' array
3228 + $all_options = get_option('mxchat_options', []);
3229 +
3230 + // Retrieve the saved message or use the default value
3231 + $default_message = 'Rate limit exceeded. Please try again later.';
3232 + $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
3233 +
3234 + // Output the textarea
3235 + echo '<div class="pro-feature-wrapper active">';
3236 + printf(
3237 + '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
3238 + esc_textarea($rate_limit_message)
3239 + );
3240 + echo '<p class="description">This message will be displayed when a user exceeds the rate limit.</p>';
3241 + echo '</div>';
3242 +}
3243 +
3244 +
3245 +public function mxchat_enable_woocommerce_integration_callback() {
3246 + // Load from mxchat_options array
3247 + $options = get_option('mxchat_options', []);
3248 +
3249 + // Get WooCommerce integration status with backwards compatibility
3250 + $woo_integration = isset($options['enable_woocommerce_integration'])
3251 + ? $options['enable_woocommerce_integration']
3252 + : '';
3253 +
3254 + // Support both old ('1') and new ('on') values
3255 + $checked = ($woo_integration === 'on' || $woo_integration === '1') ? 'checked' : '';
3256 +
3257 + // Check if the feature is activated (paid feature)
3258 + $disabled = $this->is_activated ? '' : 'disabled';
3259 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3260 +
3261 + echo '<div class="' . esc_attr($class) . '">';
3262 +
3263 + echo '<label class="toggle-switch">';
3264 + echo sprintf(
3265 + '<input type="checkbox" id="enable_woocommerce_integration" name="enable_woocommerce_integration" value="on" %s %s />',
3266 + esc_attr($checked),
3267 + esc_attr($disabled)
3268 + );
3269 + echo '<span class="slider"></span>';
3270 + echo '</label>';
3271 +
3272 + echo '<p class="description">Automatically syncs new product additions, updates, and removals to the knowledge base. For existing products, submit your product sitemap in the Knowledge Base section to add them initially.</p>';
3273 +
3274 + // Pro feature overlay for non-activated users
3275 + if (!$this->is_activated) {
3276 + echo '<div class="pro-feature-overlay">';
3277 + echo '<a href="https://mxchat.ai/" target="_blank">';
3278 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3279 + echo '</a>';
3280 + echo '</div>';
3281 + }
3282 +
3283 + echo '</div>';
3284 +}
3285 +
3286 +
3287 +
3288 +
3289 + private function mxchat_add_option_field($id, $title, $callback = '') {
3290 + add_settings_field(
3291 + $id,
3292 + $title,
3293 + $callback ? array($this, $callback) : array($this, $id . '_callback'),
3294 + 'mxchat-max',
3295 + 'mxchat_setting_section_id',
3296 + $id === 'model' ? ['label_for' => 'model'] : []
3297 + );
3298 + }
3299 +
3300 +public function api_key_callback() {
3301 + // Retrieve from your stored 'api_key' in the mxchat_options array
3302 + $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3303 +
3304 + // Notice we changed the name to "api_key" (no array notation).
3305 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
3306 + echo '<button type="button" id="toggleApiKeyVisibility">Show</button>';
3307 + echo '<p class="description">The OpenAI API key is required even when using other models as it is used for vector embedding functionality.</p>';
3308 +}
3309 +
3310 +
3311 +
3312 +
3313 +public function xai_api_key_callback() {
3314 + // Check if the feature is activated (paid feature)
3315 + $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3316 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3317 +
3318 + // Retrieve the X.AI API key value
3319 + $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3320 +
3321 + // Render the input field for the X.AI API key
3322 + echo '<div class="' . esc_attr($class) . '">';
3323 + printf(
3324 + '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
3325 + $xaiApiKey,
3326 + $disabled
3327 + );
3328 + echo '<button type="button" id="toggleXaiApiKeyVisibility">Show</button>';
3329 +
3330 + // If the feature is not activated, show the overlay with a "Pro Only" message
3331 + if (!$this->is_activated) {
3332 + echo '<div class="pro-feature-overlay">';
3333 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3334 + echo '</div>';
3335 + }
3336 +
3337 + echo '</div>';
3338 +}
3339 +
3340 +
3341 +public function claude_api_key_callback() {
3342 + // Check if the feature is activated (paid feature)
3343 + $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3344 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3345 +
3346 + // Retrieve the Claude API key value
3347 + $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3348 +
3349 + // Render the input field for the Claude API key
3350 + echo '<div class="' . esc_attr($class) . '">';
3351 + printf(
3352 + '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
3353 + $claudeApiKey,
3354 + $disabled
3355 + );
3356 + echo '<button type="button" id="toggleClaudeApiKeyVisibility">Show</button>';
3357 +
3358 + // If the feature is not activated, show the overlay with a "Pro Only" message
3359 + if (!$this->is_activated) {
3360 + echo '<div class="pro-feature-overlay">';
3361 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3362 + echo '</div>';
3363 + }
3364 +
3365 + echo '</div>';
3366 +}
3367 +
3368 +public function mxchat_woocommerce_consumer_key_callback() {
3369 + // Load the entire 'mxchat_options' array
3370 + $all_options = get_option('mxchat_options', []);
3371 +
3372 + // Retrieve the WooCommerce consumer key or set a default
3373 + $consumer_key = isset($all_options['woocommerce_consumer_key']) ? $all_options['woocommerce_consumer_key'] : '';
3374 +
3375 + // Check if the feature is activated (paid feature)
3376 + $disabled = $this->is_activated ? '' : 'disabled';
3377 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3378 +
3379 + echo '<div class="' . esc_attr($class) . '">';
3380 +
3381 + // Render the input field
3382 + printf(
3383 + '<input type="text" id="woocommerce_consumer_key" name="woocommerce_consumer_key" value="%s" class="regular-text" %s />',
3384 + esc_attr($consumer_key),
3385 + esc_attr($disabled)
3386 + );
3387 +
3388 + // Description for the input field
3389 + echo '<p class="description">Enter your WooCommerce consumer key for integration.</p>';
3390 +
3391 + // Pro feature overlay for non-activated users
3392 + if (!$this->is_activated) {
3393 + echo '<div class="pro-feature-overlay">';
3394 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
3395 + echo '</div>';
3396 + }
3397 +
3398 + echo '</div>';
3399 +}
3400 +
3401 +
3402 +public function mxchat_woocommerce_consumer_secret_callback() {
3403 + // Get value with fallback
3404 + $consumer_secret = isset($this->options['woocommerce_consumer_secret'])
3405 + ? esc_attr($this->options['woocommerce_consumer_secret'])
3406 + : '';
3407 +
3408 + // Check if the feature is activated (paid feature)
3409 + $disabled = $this->is_activated ? '' : 'disabled';
3410 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3411 +
3412 + echo '<div class="' . esc_attr($class) . '">';
3413 +
3414 + // Output the password input
3415 + echo sprintf(
3416 + '<input type="password"
3417 + id="woocommerce_consumer_secret"
3418 + name="woocommerce_consumer_secret"
3419 + value="%s"
3420 + class="regular-text"
3421 + %s />',
3422 + $consumer_secret,
3423 + esc_attr($disabled)
3424 + );
3425 +
3426 + // Add show/hide button
3427 + echo '<button type="button" id="toggleWooCommerceSecretVisibility">Show</button>';
3428 +
3429 + // Pro feature overlay
3430 + if (!$this->is_activated) {
3431 + echo '<div class="pro-feature-overlay">';
3432 + echo '<a href="https://mxchat.ai/" target="_blank">';
3433 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3434 + echo '</a>';
3435 + echo '</div>';
3436 + }
3437 +
3438 + echo '</div>';
3439 +}
3440 +public function mxchat_loops_api_key_callback() {
3441 + // Support both old and new format
3442 + $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
3443 +
3444 + echo '<div class="api-key-wrapper">';
3445 + echo sprintf(
3446 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
3447 + $loops_api_key
3448 + );
3449 + echo '<button type="button" id="toggleLoopsApiKeyVisibility">Show</button>';
3450 + echo '</div>';
3451 + echo '<p class="description">Enter your Loops API Key here. (See FAQ for details)</p>';
3452 +}
3453 +
3454 +public function mxchat_loops_mailing_list_callback() {
3455 + // Add error handling and type checking
3456 + $loops_api_key = '';
3457 + $selected_list = '';
3458 +
3459 + // Safely get the API key
3460 + if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
3461 + $loops_api_key = $this->options['loops_api_key'];
3462 + }
3463 +
3464 + // Safely get the selected list
3465 + if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
3466 + $selected_list = $this->options['loops_mailing_list'];
3467 + }
3468 +
3469 + if (!empty($loops_api_key)) {
3470 + $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
3471 + if (is_array($lists) && !empty($lists)) {
3472 + echo '<select id="loops_mailing_list" name="loops_mailing_list">';
3473 + foreach ($lists as $list) {
3474 + if (is_array($list) && isset($list['id']) && isset($list['name'])) {
3475 + echo sprintf(
3476 + '<option value="%s" %s>%s</option>',
3477 + esc_attr($list['id']),
3478 + selected($selected_list, $list['id'], false),
3479 + esc_html($list['name'])
3480 + );
3481 + }
3482 + }
3483 + echo '</select>';
3484 + } else {
3485 + echo '<p class="description">No lists found. Please verify your API Key.</p>';
3486 + }
3487 + } else {
3488 + echo '<p class="description">Enter a valid Loops API Key to load mailing lists.</p>';
3489 + }
3490 +}
3491 +public function mxchat_triggered_phrase_response_callback() {
3492 + $default_response = 'Would you like to join our mailing list? Please provide your email below.';
3493 + $triggered_response = isset($this->options['triggered_phrase_response'])
3494 + ? $this->options['triggered_phrase_response']
3495 + : $default_response;
3496 +
3497 + echo sprintf(
3498 + '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
3499 + esc_textarea($triggered_response)
3500 + );
3501 + echo '<p class="description">Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.</p>';
3502 +}
3503 +
3504 +public function mxchat_email_capture_response_callback() {
3505 + $default_response = 'Thank you for providing your email! You\'ve been added to our list.';
3506 + $email_capture_response = isset($this->options['email_capture_response'])
3507 + ? $this->options['email_capture_response']
3508 + : $default_response;
3509 +
3510 + echo sprintf(
3511 + '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
3512 + esc_textarea($email_capture_response)
3513 + );
3514 + echo '<p class="description">Enter the message to send when a user provides their email.</p>';
3515 +}
3516 +
3517 +public function mxchat_pre_chat_message_callback() {
3518 + // Load the entire 'mxchat_options' array
3519 + $all_options = get_option('mxchat_options', []);
3520 +
3521 + // Retrieve the saved message or use the default value
3522 + $default_message = 'Hey there! Ask me anything!';
3523 + $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
3524 +
3525 + // Output the textarea
3526 + printf(
3527 + '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
3528 + esc_textarea($pre_chat_message)
3529 + );
3530 + echo '<p class="description">Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.</p>';
3531 +}
3532 +
3533 +
3534 +
3535 +// Callback for AI Instructions textarea
3536 +public function system_prompt_instructions_callback() {
3537 + // Retrieve the current value of the system prompt instructions
3538 + $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
3539 +
3540 + // Render the textarea field
3541 + printf(
3542 + '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
3543 + $instructions
3544 + );
3545 +
3546 + // Provide a helpful description
3547 + echo '<p class="description">Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.</p>';
3548 +}
3549 +
3550 +
3551 +public function mxchat_model_callback() {
3552 + // Define available models grouped by provider
3553 + $models = array(
3554 + 'X.AI Models' => array(
3555 + 'grok-beta' => 'grok-beta (Early Beta)',
3556 + 'grok-2' => 'Grok 2'
3557 + ),
3558 + 'Claude Models' => array(
3559 + 'claude-3-5-sonnet-20241022' => 'Claude 3.5 Sonnet (Most Intelligent)',
3560 + 'claude-3-opus-20240229' => 'Claude 3 Opus (Highly Complex Tasks)',
3561 + 'claude-3-sonnet-20240229' => 'Claude 3 Sonnet (Balanced)',
3562 + 'claude-3-haiku-20240307' => 'Claude 3 Haiku (Fastest)'
3563 + ),
3564 + 'OpenAI Models' => array(
3565 + 'gpt-4o' => 'GPT-4o (Recommended)',
3566 + 'gpt-4o-mini' => 'GPT-4o Mini (Fast and Lightweight)',
3567 + 'gpt-4-turbo' => 'GPT-4 Turbo (High-Performance)',
3568 + 'gpt-4' => 'GPT-4 (High Intelligence)',
3569 + 'gpt-3.5-turbo' => 'GPT-3.5 Turbo (Affordable and Fast)'
3570 + )
3571 + );
3572 +
3573 + // Retrieve the currently selected model from saved options
3574 + $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-3.5-turbo';
3575 +
3576 + // Begin the select dropdown
3577 + echo '<select id="model" name="model">'; // No array notation in name attribute
3578 +
3579 + // Iterate over groups of models
3580 + foreach ($models as $group_label => $group_models) {
3581 + echo '<optgroup label="' . esc_attr($group_label) . '">';
3582 +
3583 + foreach ($group_models as $model_value => $model_label) {
3584 + // Disable Pro-only models for non-activated users
3585 + $disabled = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? 'disabled' : '';
3586 + $label_suffix = (!$this->is_activated && ($group_label === 'X.AI Models' || $group_label === 'Claude Models')) ? ' (Pro Only)' : '';
3587 +
3588 + // Output the option element
3589 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
3590 + }
3591 +
3592 + echo '</optgroup>';
3593 + }
3594 +
3595 + // Close the select dropdown
3596 + echo '</select>';
3597 +
3598 + // Add a description below the dropdown
3599 + echo '<p class="description">Select the AI model to use for your chatbot. Pro-only models are marked accordingly.</p>';
3600 +}
3601 +
3602 +
3603 +
3604 +public function mxchat_top_bar_title_callback() {
3605 + // Retrieve the current value of the top bar title from saved options
3606 + $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
3607 +
3608 + // Render the input field
3609 + echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
3610 +
3611 + // Add a description
3612 + echo '<p class="description">Enter the title text that will appear on the top bar of the chatbot.</p>';
3613 +}
3614 +
3615 +public function enable_email_block_callback() {
3616 + // Load full plugin options array
3617 + $all_options = get_option('mxchat_options', []);
3618 +
3619 + // Get the value, default to 'off'
3620 + $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
3621 +
3622 + // Check if it's 'on'
3623 + $checked = ($enable_email_block === 'on') ? 'checked' : '';
3624 +
3625 + echo '<label class="toggle-switch">';
3626 + echo sprintf(
3627 + '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
3628 + esc_attr($checked)
3629 + );
3630 + echo '<span class="slider"></span>';
3631 + echo '</label>';
3632 + echo '<p class="description">Enable to require email before a user chats. Their email will appear at the top of the transcript.</p>';
3633 +}
3634 +
3635 +
3636 +
3637 +public function email_blocker_header_content_callback() {
3638 + // Load the entire 'mxchat_options' array
3639 + $all_options = get_option('mxchat_options', []);
3640 +
3641 + // Retrieve the saved content or default to empty
3642 + $content = isset($all_options['email_blocker_header_content'])
3643 + ? $all_options['email_blocker_header_content']
3644 + : '';
3645 +
3646 + // Render the textarea
3647 + echo '<textarea
3648 + id="email_blocker_header_content"
3649 + name="email_blocker_header_content"
3650 + rows="5"
3651 + cols="70"
3652 + >' . esc_textarea($content) . '</textarea>';
3653 +
3654 + echo '<p class="description">';
3655 + echo 'You may enter HTML here, such as &lt;h2&gt;Welcome&lt;/h2&gt; or &lt;p&gt;Let\'s get started&lt;/p&gt;.';
3656 + echo ' Email form will show for users who are not logged in or have not provided an email within 24h.';
3657 + echo '</p>';
3658 +}
3659 +
3660 +
3661 +public function email_blocker_button_text_callback() {
3662 + // Load the entire 'mxchat_options' array
3663 + $all_options = get_option('mxchat_options', []);
3664 +
3665 + // Retrieve the saved button text or default to empty
3666 + $button_text = isset($all_options['email_blocker_button_text'])
3667 + ? $all_options['email_blocker_button_text']
3668 + : '';
3669 +
3670 + // Use esc_attr to safely render the existing text
3671 + echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
3672 +
3673 + echo '<p class="description">';
3674 + echo 'Enter the text you want on the submit button, e.g. "Start Chat".';
3675 + echo '</p>';
3676 +}
3677 +
3678 +
3679 +
3680 +public function mxchat_intro_message_callback() {
3681 + // Load the entire 'mxchat_options' array
3682 + $all_options = get_option('mxchat_options', []);
3683 +
3684 + // Retrieve the saved intro message or use the default
3685 + $default_message = 'Hello! How can I assist you today?';
3686 + $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
3687 +
3688 + // Output the textarea with the saved value
3689 + ?>
3690 + <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
3691 + <p class="description">
3692 + <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
3693 + </p>
3694 + <?php
3695 +}
3696 +
3697 +
3698 +public function mxchat_input_copy_callback() {
3699 + // Load the entire 'mxchat_options' array
3700 + $all_options = get_option('mxchat_options', []);
3701 +
3702 + // Retrieve the saved input copy or use the default value
3703 + $default_copy = 'How can I assist?';
3704 + $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
3705 +
3706 + // Output the input field with the saved value
3707 + printf(
3708 + '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="How can I assist?" />',
3709 + esc_attr($input_copy)
3710 + );
3711 +
3712 + // Output the description
3713 + echo '<p class="description">This is the placeholder text for the chat input field.</p>';
3714 +}
3715 +
3716 +
3717 +
3718 +
3719 +
3720 +public function mxchat_close_button_color_callback() {
3721 + $disabled = $this->is_activated ? '' : 'disabled';
3722 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3723 +
3724 + echo '<div class="' . esc_attr($class) . '">';
3725 + echo sprintf(
3726 + '<input type="text"
3727 + id="close_button_color"
3728 + name="close_button_color"
3729 + value="%s"
3730 + class="my-color-field"
3731 + data-default-color="#4a4a4a"
3732 + %s />',
3733 + isset($this->options['close_button_color']) ? esc_attr($this->options['close_button_color']) : '#4a4a4a',
3734 + esc_attr($disabled)
3735 + );
3736 +
3737 + if (!$this->is_activated) {
3738 + echo '<div class="pro-feature-overlay">';
3739 + echo '<a href="https://mxchat.ai/" target="_blank">';
3740 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3741 + echo '</a>';
3742 + echo '</div>';
3743 + }
3744 + echo '</div>';
3745 +}
3746 +
3747 +public function mxchat_chatbot_bg_color_callback() {
3748 + $disabled = $this->is_activated ? '' : 'disabled';
3749 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3750 +
3751 + echo '<div class="' . esc_attr($class) . '">';
3752 + echo sprintf(
3753 + '<input type="text"
3754 + id="chatbot_bg_color"
3755 + name="chatbot_bg_color"
3756 + value="%s"
3757 + class="my-color-field"
3758 + data-default-color="#f9f9f9"
3759 + %s />',
3760 + isset($this->options['chatbot_bg_color']) ? esc_attr($this->options['chatbot_bg_color']) : '#f9f9f9',
3761 + esc_attr($disabled)
3762 + );
3763 +
3764 + if (!$this->is_activated) {
3765 + echo '<div class="pro-feature-overlay">';
3766 + echo '<a href="https://mxchat.ai/" target="_blank">';
3767 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3768 + echo '</a>';
3769 + echo '</div>';
3770 + }
3771 + echo '</div>';
3772 +}
3773 +
3774 +public function mxchat_user_message_bg_color_callback() {
3775 + $disabled = $this->is_activated ? '' : 'disabled';
3776 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3777 +
3778 + echo '<div class="' . esc_attr($class) . '">';
3779 + echo sprintf(
3780 + '<input type="text"
3781 + id="user_message_bg_color"
3782 + name="user_message_bg_color"
3783 + value="%s"
3784 + class="my-color-field"
3785 + data-default-color="#0078d7"
3786 + %s />',
3787 + isset($this->options['user_message_bg_color']) ? esc_attr($this->options['user_message_bg_color']) : '#0078d7',
3788 + esc_attr($disabled)
3789 + );
3790 +
3791 + if (!$this->is_activated) {
3792 + echo '<div class="pro-feature-overlay">';
3793 + echo '<a href="https://mxchat.ai/" target="_blank">';
3794 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3795 + echo '</a>';
3796 + echo '</div>';
3797 + }
3798 + echo '</div>';
3799 +}
3800 +
3801 +public function mxchat_user_message_font_color_callback() {
3802 + $disabled = $this->is_activated ? '' : 'disabled';
3803 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3804 +
3805 + echo '<div class="' . esc_attr($class) . '">';
3806 + echo sprintf(
3807 + '<input type="text"
3808 + id="user_message_font_color"
3809 + name="user_message_font_color"
3810 + value="%s"
3811 + class="my-color-field"
3812 + data-default-color="#ffffff"
3813 + %s />',
3814 + isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
3815 + esc_attr($disabled)
3816 + );
3817 +
3818 + if (!$this->is_activated) {
3819 + echo '<div class="pro-feature-overlay">';
3820 + echo '<a href="https://mxchat.ai/" target="_blank">';
3821 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3822 + echo '</a>';
3823 + echo '</div>';
3824 + }
3825 + echo '</div>';
3826 +}
3827 +
3828 +public function mxchat_bot_message_bg_color_callback() {
3829 + $disabled = $this->is_activated ? '' : 'disabled';
3830 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3831 +
3832 + echo '<div class="' . esc_attr($class) . '">';
3833 + echo sprintf(
3834 + '<input type="text"
3835 + id="bot_message_bg_color"
3836 + name="bot_message_bg_color"
3837 + value="%s"
3838 + class="my-color-field"
3839 + data-default-color="#e1e1e1"
3840 + %s />',
3841 + isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
3842 + esc_attr($disabled)
3843 + );
3844 +
3845 + if (!$this->is_activated) {
3846 + echo '<div class="pro-feature-overlay">';
3847 + echo '<a href="https://mxchat.ai/" target="_blank">';
3848 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3849 + echo '</a>';
3850 + echo '</div>';
3851 + }
3852 + echo '</div>';
3853 +}
3854 +
3855 +public function mxchat_bot_message_font_color_callback() {
3856 + $disabled = $this->is_activated ? '' : 'disabled';
3857 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3858 +
3859 + echo '<div class="' . esc_attr($class) . '">';
3860 + echo sprintf(
3861 + '<input type="text"
3862 + id="bot_message_font_color"
3863 + name="bot_message_font_color"
3864 + value="%s"
3865 + class="my-color-field"
3866 + data-default-color="#333333"
3867 + %s />',
3868 + isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
3869 + esc_attr($disabled)
3870 + );
3871 +
3872 + if (!$this->is_activated) {
3873 + echo '<div class="pro-feature-overlay">';
3874 + echo '<a href="https://mxchat.ai/" target="_blank">';
3875 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3876 + echo '</a>';
3877 + echo '</div>';
3878 + }
3879 + echo '</div>';
3880 +}
3881 +
3882 +public function mxchat_live_agent_message_bg_color_callback() {
3883 + $disabled = $this->is_activated ? '' : 'disabled';
3884 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3885 +
3886 + echo '<div class="' . esc_attr($class) . '">';
3887 + echo sprintf(
3888 + '<input type="text"
3889 + id="live_agent_message_bg_color"
3890 + name="live_agent_message_bg_color"
3891 + value="%s"
3892 + class="my-color-field"
3893 + data-default-color="#ffffff"
3894 + %s />',
3895 + isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
3896 + esc_attr($disabled)
3897 + );
3898 +
3899 + if (!$this->is_activated) {
3900 + echo '<div class="pro-feature-overlay">';
3901 + echo '<a href="https://mxchat.ai/" target="_blank">';
3902 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3903 + echo '</a>';
3904 + echo '</div>';
3905 + }
3906 + echo '</div>';
3907 +}
3908 +
3909 +public function mxchat_live_agent_message_font_color_callback() {
3910 + $disabled = $this->is_activated ? '' : 'disabled';
3911 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3912 +
3913 + echo '<div class="' . esc_attr($class) . '">';
3914 + echo sprintf(
3915 + '<input type="text"
3916 + id="live_agent_message_font_color"
3917 + name="live_agent_message_font_color"
3918 + value="%s"
3919 + class="my-color-field"
3920 + data-default-color="#333333"
3921 + %s />',
3922 + isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
3923 + esc_attr($disabled)
3924 + );
3925 +
3926 + if (!$this->is_activated) {
3927 + echo '<div class="pro-feature-overlay">';
3928 + echo '<a href="https://mxchat.ai/" target="_blank">';
3929 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3930 + echo '</a>';
3931 + echo '</div>';
3932 + }
3933 + echo '</div>';
3934 +}
3935 +
3936 +public function mxchat_mode_indicator_bg_color_callback() {
3937 + $disabled = $this->is_activated ? '' : 'disabled';
3938 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3939 +
3940 + echo '<div class="' . esc_attr($class) . '">';
3941 + echo sprintf(
3942 + '<input type="text"
3943 + id="mode_indicator_bg_color"
3944 + name="mode_indicator_bg_color"
3945 + value="%s"
3946 + class="my-color-field"
3947 + data-default-color="#767676"
3948 + %s />',
3949 + isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
3950 + esc_attr($disabled)
3951 + );
3952 +
3953 + if (!$this->is_activated) {
3954 + echo '<div class="pro-feature-overlay">';
3955 + echo '<a href="https://mxchat.ai/" target="_blank">';
3956 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3957 + echo '</a>';
3958 + echo '</div>';
3959 + }
3960 + echo '</div>';
3961 +}
3962 +
3963 +public function mxchat_mode_indicator_font_color_callback() {
3964 + $disabled = $this->is_activated ? '' : 'disabled';
3965 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3966 +
3967 + echo '<div class="' . esc_attr($class) . '">';
3968 + echo sprintf(
3969 + '<input type="text"
3970 + id="mode_indicator_font_color"
3971 + name="mode_indicator_font_color"
3972 + value="%s"
3973 + class="my-color-field"
3974 + data-default-color="#ffffff"
3975 + %s />',
3976 + isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
3977 + esc_attr($disabled)
3978 + );
3979 +
3980 + if (!$this->is_activated) {
3981 + echo '<div class="pro-feature-overlay">';
3982 + echo '<a href="https://mxchat.ai/" target="_blank">';
3983 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
3984 + echo '</a>';
3985 + echo '</div>';
3986 + }
3987 + echo '</div>';
3988 +}
3989 +
3990 +public function mxchat_toolbar_icon_color_callback() {
3991 + $disabled = $this->is_activated ? '' : 'disabled';
3992 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3993 +
3994 + echo '<div class="' . esc_attr($class) . '">';
3995 + echo sprintf(
3996 + '<input type="text"
3997 + id="toolbar_icon_color"
3998 + name="toolbar_icon_color"
3999 + value="%s"
4000 + class="my-color-field"
4001 + data-default-color="#212121"
4002 + %s />',
4003 + isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
4004 + esc_attr($disabled)
4005 + );
4006 +
4007 + if (!$this->is_activated) {
4008 + echo '<div class="pro-feature-overlay">';
4009 + echo '<a href="https://mxchat.ai/" target="_blank">';
4010 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4011 + echo '</a>';
4012 + echo '</div>';
4013 + }
4014 + echo '</div>';
4015 +}
4016 +
4017 +public function mxchat_top_bar_bg_color_callback() {
4018 + $disabled = $this->is_activated ? '' : 'disabled';
4019 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4020 +
4021 + echo '<div class="' . esc_attr($class) . '">';
4022 + echo sprintf(
4023 + '<input type="text"
4024 + id="top_bar_bg_color"
4025 + name="top_bar_bg_color"
4026 + value="%s"
4027 + class="my-color-field"
4028 + data-default-color="#00b294"
4029 + %s />',
4030 + isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
4031 + esc_attr($disabled)
4032 + );
4033 +
4034 + if (!$this->is_activated) {
4035 + echo '<div class="pro-feature-overlay">';
4036 + echo '<a href="https://mxchat.ai/" target="_blank">';
4037 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4038 + echo '</a>';
4039 + echo '</div>';
4040 + }
4041 + echo '</div>';
4042 +}
4043 +
4044 +public function mxchat_send_button_font_color_callback() {
4045 + $disabled = $this->is_activated ? '' : 'disabled';
4046 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4047 +
4048 + echo '<div class="' . esc_attr($class) . '">';
4049 + echo sprintf(
4050 + '<input type="text"
4051 + id="send_button_font_color"
4052 + name="send_button_font_color"
4053 + value="%s"
4054 + class="my-color-field"
4055 + data-default-color="#ffffff"
4056 + %s />',
4057 + isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
4058 + esc_attr($disabled)
4059 + );
4060 +
4061 + if (!$this->is_activated) {
4062 + echo '<div class="pro-feature-overlay">';
4063 + echo '<a href="https://mxchat.ai/" target="_blank">';
4064 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4065 + echo '</a>';
4066 + echo '</div>';
4067 + }
4068 + echo '</div>';
4069 +}
4070 +
4071 +public function mxchat_chatbot_background_color_callback() {
4072 + $disabled = $this->is_activated ? '' : 'disabled';
4073 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4074 +
4075 + echo '<div class="' . esc_attr($class) . '">';
4076 + echo sprintf(
4077 + '<input type="text"
4078 + id="chatbot_background_color"
4079 + name="chatbot_background_color"
4080 + value="%s"
4081 + class="my-color-field"
4082 + data-default-color="#000000"
4083 + %s />',
4084 + isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
4085 + esc_attr($disabled)
4086 + );
4087 +
4088 + if (!$this->is_activated) {
4089 + echo '<div class="pro-feature-overlay">';
4090 + echo '<a href="https://mxchat.ai/" target="_blank">';
4091 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4092 + echo '</a>';
4093 + echo '</div>';
4094 + }
4095 + echo '</div>';
4096 +}
4097 +
4098 +public function mxchat_icon_color_callback() {
4099 + $disabled = $this->is_activated ? '' : 'disabled';
4100 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4101 +
4102 + echo '<div class="' . esc_attr($class) . '">';
4103 + echo sprintf(
4104 + '<input type="text"
4105 + id="icon_color"
4106 + name="icon_color"
4107 + value="%s"
4108 + class="my-color-field"
4109 + data-default-color="#ffffff"
4110 + %s />',
4111 + isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
4112 + esc_attr($disabled)
4113 + );
4114 +
4115 + if (!$this->is_activated) {
4116 + echo '<div class="pro-feature-overlay">';
4117 + echo '<a href="https://mxchat.ai/" target="_blank">';
4118 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4119 + echo '</a>';
4120 + echo '</div>';
4121 + }
4122 + echo '</div>';
4123 +}
4124 +
4125 +public function mxchat_custom_icon_callback() {
4126 + $disabled = $this->is_activated ? '' : 'disabled';
4127 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4128 + $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
4129 +
4130 + echo '<div class="' . esc_attr($class) . '">';
4131 + echo sprintf(
4132 + '<input type="url"
4133 + id="custom_icon"
4134 + name="custom_icon"
4135 + value="%s"
4136 + placeholder="Enter PNG URL"
4137 + class="regular-text"
4138 + %s />',
4139 + $custom_icon_url,
4140 + esc_attr($disabled)
4141 + );
4142 +
4143 + // Preview container for the icon
4144 + if (!empty($custom_icon_url)) {
4145 + echo '<div class="icon-preview" style="margin-top: 10px;">';
4146 + echo '<img src="' . esc_url($custom_icon_url) . '" alt="Custom Icon Preview" style="max-width: 48px; height: auto;" />';
4147 + echo '</div>';
4148 + }
4149 +
4150 + echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
4151 +
4152 + if (!$this->is_activated) {
4153 + echo '<div class="pro-feature-overlay">';
4154 + echo '<a href="https://mxchat.ai/" target="_blank">';
4155 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4156 + echo '</a>';
4157 + echo '</div>';
4158 + }
4159 + echo '</div>';
4160 +}
4161 +
4162 +public function mxchat_title_icon_callback() {
4163 + $disabled = $this->is_activated ? '' : 'disabled';
4164 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4165 + // Fixed the variable reference - it was using custom_icon instead of title_icon
4166 + $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
4167 +
4168 + echo '<div class="' . esc_attr($class) . '">';
4169 + echo sprintf(
4170 + '<input type="url"
4171 + id="title_icon"
4172 + name="title_icon"
4173 + value="%s"
4174 + placeholder="Enter PNG URL"
4175 + class="regular-text"
4176 + %s />',
4177 + $title_icon_url,
4178 + esc_attr($disabled)
4179 + );
4180 +
4181 + // Preview container for the icon
4182 + if (!empty($title_icon_url)) {
4183 + echo '<div class="icon-preview" style="margin-top: 10px;">';
4184 + echo '<img src="' . esc_url($title_icon_url) . '" alt="Title Icon Preview" style="max-width: 48px; height: auto;" />';
4185 + echo '</div>';
4186 + }
4187 +
4188 + echo '<p class="description">Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.</p>';
4189 +
4190 + if (!$this->is_activated) {
4191 + echo '<div class="pro-feature-overlay">';
4192 + echo '<a href="https://mxchat.ai/" target="_blank">';
4193 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4194 + echo '</a>';
4195 + echo '</div>';
4196 + }
4197 + echo '</div>';
4198 +}
4199 +
4200 +public function mxchat_chat_input_font_color_callback() {
4201 + $disabled = $this->is_activated ? '' : 'disabled';
4202 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4203 +
4204 + echo '<div class="' . esc_attr($class) . '">';
4205 + echo sprintf(
4206 + '<input type="text"
4207 + id="chat_input_font_color"
4208 + name="chat_input_font_color"
4209 + value="%s"
4210 + class="my-color-field"
4211 + data-default-color="#555555"
4212 + %s />',
4213 + isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
4214 + esc_attr($disabled)
4215 + );
4216 +
4217 + if (!$this->is_activated) {
4218 + echo '<div class="pro-feature-overlay">';
4219 + echo '<a href="https://mxchat.ai/" target="_blank">';
4220 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4221 + echo '</a>';
4222 + echo '</div>';
4223 + }
4224 + echo '</div>';
4225 +}
4226 +
4227 +public function mxchat_append_to_body_callback() {
4228 + // Get value from options array, default to 'off'
4229 + $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
4230 + $checked = ($append_to_body === 'on') ? 'checked' : '';
4231 +
4232 + echo '<label class="toggle-switch">';
4233 + echo sprintf(
4234 + '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
4235 + esc_attr($checked)
4236 + );
4237 + echo '<span class="slider"></span>';
4238 + echo '</label>';
4239 + 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>';
4240 +}
4241 +
4242 +
4243 +
4244 +public function mxchat_privacy_toggle_callback() {
4245 + // Load from mxchat_options array
4246 + $options = get_option('mxchat_options', []);
4247 +
4248 + // Get privacy toggle value with fallback
4249 + $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
4250 + $checked = ($privacy_toggle === 'on') ? 'checked' : '';
4251 +
4252 + // Get privacy text with fallback
4253 + $privacy_text = isset($options['privacy_text'])
4254 + ? $options['privacy_text']
4255 + : 'By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.';
4256 +
4257 + // Output the toggle switch
4258 + echo '<label class="toggle-switch">';
4259 + echo sprintf(
4260 + '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
4261 + esc_attr($checked)
4262 + );
4263 + echo '<span class="slider"></span>';
4264 + echo '</label>';
4265 + echo '<p class="description">Enable this option to display a privacy notice below the chat widget.</p>';
4266 +
4267 + // Output the custom text input field
4268 + echo sprintf(
4269 + '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
4270 + esc_textarea($privacy_text)
4271 + );
4272 + echo '<p class="description">Enter the privacy policy text. You can include HTML links.</p>';
4273 +}
4274 +
4275 +
4276 +public function mxchat_complianz_toggle_callback() {
4277 + // Load from mxchat_options array
4278 + $options = get_option('mxchat_options', []);
4279 +
4280 + // Get complianz toggle value with fallback
4281 + $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
4282 + $checked = ($complianz_toggle === 'on') ? 'checked' : '';
4283 +
4284 + // Check if the plugin is activated (paid feature)
4285 + $disabled = $this->is_activated ? '' : 'disabled';
4286 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4287 +
4288 + echo '<div class="' . esc_attr($class) . '">';
4289 +
4290 + // Output the toggle switch
4291 + echo '<label class="toggle-switch">';
4292 + echo sprintf(
4293 + '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s %s />',
4294 + esc_attr($checked),
4295 + esc_attr($disabled)
4296 + );
4297 + echo '<span class="slider"></span>';
4298 + echo '</label>';
4299 +
4300 + echo '<p class="description">Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).</p>';
4301 +
4302 + // If the feature is not activated, show the Pro feature overlay
4303 + if (!$this->is_activated) {
4304 + echo '<div class="pro-feature-overlay">';
4305 + echo '<a href="https://mxchat.ai/" target="_blank">';
4306 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4307 + echo '</a>';
4308 + echo '</div>';
4309 + }
4310 +
4311 + echo '</div>';
4312 +}
4313 +
4314 +
4315 +public function mxchat_link_target_toggle_callback() {
4316 + // Load from mxchat_options array
4317 + $options = get_option('mxchat_options', []);
4318 +
4319 + // Get link target toggle value with fallback
4320 + $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
4321 + $checked = ($link_target_toggle === 'on') ? 'checked' : '';
4322 +
4323 + // Output the toggle switch
4324 + echo '<label class="toggle-switch">';
4325 + echo sprintf(
4326 + '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
4327 + esc_attr($checked)
4328 + );
4329 + echo '<span class="slider"></span>';
4330 + echo '</label>';
4331 + echo '<p class="description">Enable to open links in a new tab (default is to open in the same tab).</p>';
4332 +}
4333 +
4334 +public function mxchat_chat_persistence_toggle_callback() {
4335 + // Load from mxchat_options array
4336 + $options = get_option('mxchat_options', []);
4337 +
4338 + // Get chat persistence toggle value with fallback
4339 + $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
4340 + $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
4341 +
4342 + // Check if the plugin is activated (paid feature)
4343 + $disabled = $this->is_activated ? '' : 'disabled';
4344 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4345 +
4346 + echo '<div class="' . esc_attr($class) . '">';
4347 +
4348 + // Output the toggle switch
4349 + echo '<label class="toggle-switch">';
4350 + echo sprintf(
4351 + '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s %s />',
4352 + esc_attr($checked),
4353 + esc_attr($disabled)
4354 + );
4355 + echo '<span class="slider"></span>';
4356 + echo '</label>';
4357 +
4358 + echo '<p class="description">Enable to keep chat history when users navigate tabs or return to the site within 24 hours.</p>';
4359 +
4360 + // If not activated, show Pro feature overlay
4361 + if (!$this->is_activated) {
4362 + echo '<div class="pro-feature-overlay">';
4363 + echo '<a href="https://mxchat.ai/" target="_blank">';
4364 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4365 + echo '</a>';
4366 + echo '</div>';
4367 + }
4368 +
4369 + echo '</div>';
4370 +}
4371 +
4372 +
4373 +public function mxchat_popular_question_1_callback() {
4374 + // Load the full plugin options array
4375 + $all_options = get_option('mxchat_options', []);
4376 +
4377 + // Retrieve the specific option for popular_question_1
4378 + $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
4379 +
4380 + // Render the input field
4381 + printf(
4382 + '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="Enter Popular Question 1" class="regular-text" />',
4383 + esc_attr($popular_question_1)
4384 + );
4385 +
4386 + // Add a description for the field
4387 + echo '<p class="description">This will be the first popular question in the chatbot.</p>';
4388 +}
4389 +
4390 +
4391 +public function mxchat_popular_question_2_callback() {
4392 + // Load the full plugin options array
4393 + $all_options = get_option('mxchat_options', []);
4394 +
4395 + // Retrieve the specific option for popular_question_2
4396 + $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
4397 +
4398 + // Check if the plugin is activated (paid feature)
4399 + $disabled = $this->is_activated ? '' : 'disabled';
4400 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4401 +
4402 + echo '<div class="' . esc_attr($class) . '">';
4403 +
4404 + // Render the input field
4405 + printf(
4406 + '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="Enter Popular Question 2" class="regular-text" %s />',
4407 + esc_attr($popular_question_2),
4408 + esc_attr($disabled)
4409 + );
4410 +
4411 + // Add a description for the field
4412 + echo '<p class="description">This will be the second popular question in the chatbot.</p>';
4413 +
4414 + // If not activated, show Pro feature overlay
4415 + if (!$this->is_activated) {
4416 + echo '<div class="pro-feature-overlay">';
4417 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4418 + echo '</div>';
4419 + }
4420 +
4421 + echo '</div>';
4422 +}
4423 +
4424 +
4425 +
4426 +public function mxchat_popular_question_3_callback() {
4427 + // Load the full plugin options array
4428 + $all_options = get_option('mxchat_options', []);
4429 +
4430 + // Retrieve the specific option for popular_question_3
4431 + $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
4432 +
4433 + // Check if the plugin is activated (paid feature)
4434 + $disabled = $this->is_activated ? '' : 'disabled';
4435 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4436 +
4437 + echo '<div class="' . esc_attr($class) . '">';
4438 +
4439 + // Render the input field
4440 + printf(
4441 + '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="Enter Popular Question 3" class="regular-text" %s />',
4442 + esc_attr($popular_question_3),
4443 + esc_attr($disabled)
4444 + );
4445 +
4446 + // Add a description for the field
4447 + echo '<p class="description">This will be the third popular question in the chatbot.</p>';
4448 +
4449 + // If not activated, show Pro feature overlay
4450 + if (!$this->is_activated) {
4451 + echo '<div class="pro-feature-overlay">';
4452 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4453 + echo '</div>';
4454 + }
4455 +
4456 + echo '</div>';
4457 +}
4458 +
4459 +
4460 +public function mxchat_additional_popular_questions_callback() {
4461 + // Load from both possible sources for backwards compatibility
4462 + $options = get_option('mxchat_options', []);
4463 + $additional_questions = isset($options['additional_popular_questions'])
4464 + ? $options['additional_popular_questions']
4465 + : get_option('additional_popular_questions', array());
4466 +
4467 + // Check if the feature is activated (paid feature)
4468 + $disabled = $this->is_activated ? '' : 'disabled';
4469 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4470 +
4471 + echo '<div class="' . esc_attr($class) . '">';
4472 + echo '<div id="mxchat-additional-questions-container">';
4473 +
4474 + if (!empty($additional_questions)) {
4475 + foreach ($additional_questions as $index => $question) {
4476 + printf(
4477 + '<div class="mxchat-question-row">
4478 + <input type="text" name="additional_popular_questions[]"
4479 + value="%s"
4480 + placeholder="Enter Additional Popular Question %d"
4481 + class="regular-text mxchat-question-input"
4482 + data-question-index="%d"
4483 + %s />
4484 + <button type="button" class="button mxchat-remove-question"
4485 + aria-label="Remove question" %s>Remove</button>
4486 + </div>',
4487 + esc_attr($question),
4488 + $index + 4,
4489 + $index,
4490 + esc_attr($disabled),
4491 + esc_attr($disabled)
4492 + );
4493 + }
4494 + } else {
4495 + printf(
4496 + '<div class="mxchat-question-row">
4497 + <input type="text" name="additional_popular_questions[]"
4498 + value=""
4499 + placeholder="Enter Additional Popular Question 4"
4500 + class="regular-text mxchat-question-input"
4501 + data-question-index="0"
4502 + %s />
4503 + <button type="button" class="button mxchat-remove-question"
4504 + aria-label="Remove question" %s>Remove</button>
4505 + </div>',
4506 + esc_attr($disabled),
4507 + esc_attr($disabled)
4508 + );
4509 + }
4510 +
4511 + echo '</div>';
4512 +
4513 + printf(
4514 + '<button type="button" class="button mxchat-add-question" aria-label="Add question" %s>Add Question</button>',
4515 + esc_attr($disabled)
4516 + );
4517 +
4518 + echo '<p class="description">You can add more popular questions beyond the default three here.</p>';
4519 +
4520 + if (!$this->is_activated) {
4521 + echo '<div class="pro-feature-overlay">';
4522 + echo '<a href="https://mxchat.ai/" target="_blank">';
4523 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4524 + echo '</a>';
4525 + echo '</div>';
4526 + }
4527 +
4528 + echo '</div>';
4529 +}
4530 +
4531 +
4532 +public function mxchat_brave_api_key_callback() {
4533 + $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
4534 +
4535 + echo '<div class="api-key-wrapper">';
4536 + echo sprintf(
4537 + '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
4538 + $brave_api_key
4539 + );
4540 + echo '<button type="button" id="toggleBraveApiKeyVisibility">Show</button>';
4541 + echo '</div>';
4542 + echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
4543 +}
4544 +
4545 +public function mxchat_brave_image_count_callback() {
4546 + $brave_image_count = isset($this->options['brave_image_count'])
4547 + ? intval($this->options['brave_image_count'])
4548 + : 4;
4549 +
4550 + echo sprintf(
4551 + '<input type="number" id="brave_image_count" name="brave_image_count"
4552 + value="%d" min="1" max="6" class="small-text" />',
4553 + $brave_image_count
4554 + );
4555 + echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
4556 +}
4557 +
4558 +public function mxchat_brave_safe_search_callback() {
4559 + $brave_safe_search = isset($this->options['brave_safe_search'])
4560 + ? esc_attr($this->options['brave_safe_search'])
4561 + : 'strict';
4562 +
4563 + echo '<select id="brave_safe_search" name="brave_safe_search">';
4564 + echo sprintf(
4565 + '<option value="strict" %s>%s</option>',
4566 + selected($brave_safe_search, 'strict', false),
4567 + __('Strict', 'mxchat')
4568 + );
4569 + echo sprintf(
4570 + '<option value="off" %s>%s</option>',
4571 + selected($brave_safe_search, 'off', false),
4572 + __('Off', 'mxchat')
4573 + );
4574 + echo '</select>';
4575 + echo '<p class="description">' .
4576 + __('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
4577 + '</p>';
4578 +}
4579 +
4580 +public function mxchat_brave_news_count_callback() {
4581 + $brave_news_count = isset($this->options['brave_news_count'])
4582 + ? intval($this->options['brave_news_count'])
4583 + : 3;
4584 +
4585 + echo sprintf(
4586 + '<input type="number" id="brave_news_count" name="brave_news_count"
4587 + value="%d" min="1" max="10" class="small-text" />',
4588 + $brave_news_count
4589 + );
4590 + echo '<p class="description">' . __('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
4591 +}
4592 +
4593 +public function mxchat_brave_country_callback() {
4594 + $brave_country = isset($this->options['brave_country'])
4595 + ? esc_attr($this->options['brave_country'])
4596 + : 'us';
4597 +
4598 + echo sprintf(
4599 + '<input type="text" id="brave_country" name="brave_country"
4600 + value="%s" maxlength="2" class="small-text" />',
4601 + $brave_country
4602 + );
4603 + echo '<p class="description">' . __('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
4604 +}
4605 +
4606 +public function mxchat_brave_language_callback() {
4607 + $brave_language = isset($this->options['brave_language'])
4608 + ? esc_attr($this->options['brave_language'])
4609 + : 'en';
4610 +
4611 + echo sprintf(
4612 + '<input type="text" id="brave_language" name="brave_language"
4613 + value="%s" maxlength="2" class="small-text" />',
4614 + $brave_language
4615 + );
4616 + echo '<p class="description">' . __('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
4617 +}
4618 +
4619 +
4620 +
4621 +
4622 +
4623 +// Section Callback
4624 +public function mxchat_pdf_intent_section_callback() {
4625 + echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
4626 +}
4627 +
4628 +public function mxchat_chat_toolbar_toggle_callback() {
4629 + // Get chat toolbar toggle value with fallback
4630 + $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
4631 + $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
4632 +
4633 + // Check if the plugin is activated (paid feature)
4634 + $disabled = $this->is_activated ? '' : 'disabled';
4635 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4636 +
4637 + echo '<div class="' . esc_attr($class) . '">';
4638 +
4639 + // Output the toggle switch
4640 + echo '<label class="toggle-switch">';
4641 + echo sprintf(
4642 + '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
4643 + esc_attr($checked),
4644 + esc_attr($disabled)
4645 + );
4646 + echo '<span class="slider"></span>';
4647 + echo '</label>';
4648 +
4649 + // Pro feature overlay
4650 + if (!$this->is_activated) {
4651 + echo '<div class="pro-feature-overlay">';
4652 + echo '<a href="https://mxchat.ai/" target="_blank">';
4653 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4654 + echo '</a>';
4655 + echo '</div>';
4656 + }
4657 +
4658 + echo '</div>';
4659 + echo '<p class="description">Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).</p>';
4660 +}
4661 +
4662 +
4663 +public function mxchat_pdf_intent_trigger_text_callback() {
4664 + $disabled = $this->is_activated ? '' : 'disabled';
4665 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4666 + $default_text = "Please provide the URL to the PDF you'd like to discuss.";
4667 +
4668 + echo '<div class="' . esc_attr($class) . '">';
4669 + echo sprintf(
4670 + '<textarea id="pdf_intent_trigger_text"
4671 + name="pdf_intent_trigger_text"
4672 + rows="3"
4673 + cols="50"
4674 + placeholder="Enter trigger text"
4675 + %s>%s</textarea>',
4676 + esc_attr($disabled),
4677 + isset($this->options['pdf_intent_trigger_text'])
4678 + ? esc_textarea($this->options['pdf_intent_trigger_text'])
4679 + : esc_textarea($default_text)
4680 + );
4681 + echo '<p class="description">Text displayed when the intent is triggered.</p>';
4682 +
4683 + if (!$this->is_activated) {
4684 + echo '<div class="pro-feature-overlay">';
4685 + echo '<a href="https://mxchat.ai/" target="_blank">';
4686 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4687 + echo '</a>';
4688 + echo '</div>';
4689 + }
4690 + echo '</div>';
4691 +}
4692 +
4693 +public function mxchat_pdf_intent_success_text_callback() {
4694 + $disabled = $this->is_activated ? '' : 'disabled';
4695 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4696 + $default_text = "I've processed the PDF. What questions do you have about it?";
4697 +
4698 + echo '<div class="' . esc_attr($class) . '">';
4699 + echo sprintf(
4700 + '<textarea id="pdf_intent_success_text"
4701 + name="pdf_intent_success_text"
4702 + rows="3"
4703 + cols="50"
4704 + placeholder="Enter success text"
4705 + %s>%s</textarea>',
4706 + esc_attr($disabled),
4707 + isset($this->options['pdf_intent_success_text'])
4708 + ? esc_textarea($this->options['pdf_intent_success_text'])
4709 + : esc_textarea($default_text)
4710 + );
4711 + echo '<p class="description">Text displayed when the intent is successful.</p>';
4712 +
4713 + if (!$this->is_activated) {
4714 + echo '<div class="pro-feature-overlay">';
4715 + echo '<a href="https://mxchat.ai/" target="_blank">';
4716 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4717 + echo '</a>';
4718 + echo '</div>';
4719 + }
4720 + echo '</div>';
4721 +}
4722 +
4723 +public function mxchat_pdf_intent_error_text_callback() {
4724 + $disabled = $this->is_activated ? '' : 'disabled';
4725 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4726 + $default_text = "Sorry, I couldn't process the PDF. Please ensure it's a valid file.";
4727 +
4728 + echo '<div class="' . esc_attr($class) . '">';
4729 + echo sprintf(
4730 + '<textarea id="pdf_intent_error_text"
4731 + name="pdf_intent_error_text"
4732 + rows="3"
4733 + cols="50"
4734 + placeholder="Enter error text"
4735 + %s>%s</textarea>',
4736 + esc_attr($disabled),
4737 + isset($this->options['pdf_intent_error_text'])
4738 + ? esc_textarea($this->options['pdf_intent_error_text'])
4739 + : esc_textarea($default_text)
4740 + );
4741 + echo '<p class="description">Text displayed when an error occurs during the intent.</p>';
4742 +
4743 + if (!$this->is_activated) {
4744 + echo '<div class="pro-feature-overlay">';
4745 + echo '<a href="https://mxchat.ai/" target="_blank">';
4746 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4747 + echo '</a>';
4748 + echo '</div>';
4749 + }
4750 + echo '</div>';
4751 +}
4752 +
4753 +public function mxchat_pdf_max_pages_callback() {
4754 + $disabled = $this->is_activated ? '' : 'disabled';
4755 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4756 + $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
4757 +
4758 + echo '<div class="' . esc_attr($class) . '">';
4759 + echo sprintf(
4760 + '<input type="range"
4761 + id="pdf_max_pages"
4762 + name="pdf_max_pages"
4763 + min="1"
4764 + max="69"
4765 + value="%d"
4766 + %s
4767 + class="range-slider" />',
4768 + esc_attr($max_pages),
4769 + esc_attr($disabled)
4770 + );
4771 + echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
4772 + echo '<p class="description">Set the maximum number of document pages users can upload for processing. (1-69 pages)</p>';
4773 +
4774 + if (!$this->is_activated) {
4775 + echo '<div class="pro-feature-overlay">';
4776 + echo '<a href="https://mxchat.ai/" target="_blank">';
4777 + echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="Pro Only" />';
4778 + echo '</a>';
4779 + echo '</div>';
4780 + }
4781 + echo '</div>';
4782 +}
4783 +
4784 +
4785 +// Status Toggle Callback
4786 +public function mxchat_live_agent_status_callback() {
4787 + $disabled = $this->is_activated ? '' : 'disabled';
4788 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4789 + $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'offline';
4790 +
4791 + echo '<div class="' . esc_attr($class) . '">';
4792 + echo '<label class="mxchat-switch">';
4793 + printf(
4794 + '<input type="checkbox" id="live_agent_status" name="mxchat_options[live_agent_status]" value="online" %s %s />',
4795 + checked($status, 'online', false),
4796 + esc_attr($disabled)
4797 + );
4798 + echo '<span class="mxchat-slider"></span>';
4799 + echo '</label>';
4800 + echo '<label for="live_agent_status" class="mxchat-status-label">';
4801 + echo '<span class="status-text">' . ($status === 'online' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
4802 + echo '</label>';
4803 +
4804 + if (!$this->is_activated) {
4805 + echo '<div class="pro-feature-overlay">';
4806 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4807 + echo '</div>';
4808 + }
4809 + echo '</div>';
4810 +}
4811 +
4812 +// Away Message Callback
4813 +public function mxchat_live_agent_away_message_callback() {
4814 + $disabled = $this->is_activated ? '' : 'disabled';
4815 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4816 + $message = isset($this->options['live_agent_away_message'])
4817 + ? $this->options['live_agent_away_message']
4818 + : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
4819 +
4820 + echo '<div class="' . esc_attr($class) . '">';
4821 + printf(
4822 + '<textarea id="live_agent_away_message" name="mxchat_options[live_agent_away_message]" rows="3" cols="50" %s>%s</textarea>',
4823 + esc_attr($disabled),
4824 + esc_textarea($message)
4825 + );
4826 + echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
4827 +
4828 + if (!$this->is_activated) {
4829 + echo '<div class="pro-feature-overlay">';
4830 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4831 + echo '</div>';
4832 + }
4833 + echo '</div>';
4834 +}
4835 +public function mxchat_live_agent_notification_message_callback() {
4836 + $disabled = $this->is_activated ? '' : 'disabled';
4837 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4838 + $message = isset($this->options['live_agent_notification_message'])
4839 + ? $this->options['live_agent_notification_message']
4840 + : __('Live agent has been notified.', 'mxchat');
4841 +
4842 + echo '<div class="' . esc_attr($class) . '">';
4843 + printf(
4844 + '<textarea id="live_agent_notification_message" name="mxchat_options[live_agent_notification_message]" rows="3" cols="50" %s>%s</textarea>',
4845 + esc_attr($disabled),
4846 + esc_textarea($message)
4847 + );
4848 + echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
4849 +
4850 + if (!$this->is_activated) {
4851 + echo '<div class="pro-feature-overlay">';
4852 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4853 + echo '</div>';
4854 + }
4855 + echo '</div>';
4856 +}
4857 +
4858 +public function mxchat_live_agent_webhook_url_callback() {
4859 + $disabled = $this->is_activated ? '' : 'disabled';
4860 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4861 + $webhook_url = isset($this->options['live_agent_webhook_url'])
4862 + ? esc_url($this->options['live_agent_webhook_url'])
4863 + : esc_url(get_option('live_agent_webhook_url', ''));
4864 +
4865 + echo '<div class="' . esc_attr($class) . '">';
4866 + printf(
4867 + '<input type="password" id="live_agent_webhook_url" name="mxchat_options[live_agent_webhook_url]" value="%s" class="regular-text" %s />',
4868 + $webhook_url,
4869 + esc_attr($disabled)
4870 + );
4871 + echo '<button type="button" id="toggleWebhookUrlVisibility">Show</button>';
4872 + echo '<p class="description">Enter your Slack webhook URL for live agent notifications.</p>';
4873 + if (!$this->is_activated) {
4874 + echo '<div class="pro-feature-overlay">';
4875 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4876 + echo '</div>';
4877 + }
4878 + echo '</div>';
4879 +}
4880 +
4881 +
4882 +public function mxchat_similarity_threshold_callback() {
4883 + // Load from mxchat_options array
4884 + $options = get_option('mxchat_options', []);
4885 +
4886 + // Get value with backwards compatibility
4887 + $threshold = isset($options['similarity_threshold'])
4888 + ? $options['similarity_threshold']
4889 + : get_option('mxchat_similarity_threshold', 80);
4890 +
4891 + echo '<div class="slider-container">';
4892 + echo sprintf(
4893 + '<input type="range"
4894 + id="similarity_threshold"
4895 + name="similarity_threshold"
4896 + min="70"
4897 + max="85"
4898 + step="1"
4899 + value="%s"
4900 + class="range-slider" />',
4901 + esc_attr($threshold)
4902 + );
4903 + echo sprintf(
4904 + '<span id="threshold_value" class="range-value">%s</span>',
4905 + esc_html($threshold)
4906 + );
4907 + echo '</div>';
4908 +
4909 + echo '<p class="description">';
4910 + echo 'Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge.';
4911 + echo '</p>';
4912 +}
4913 +
4914 +
4915 +
4916 +public function mxchat_live_agent_secret_key_callback() {
4917 + $disabled = $this->is_activated ? '' : 'disabled';
4918 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4919 +
4920 + echo '<div class="' . esc_attr($class) . '">';
4921 + printf(
4922 + '<input type="password" id="live_agent_secret_key" name="mxchat_options[live_agent_secret_key]" value="%s" class="regular-text" %s />',
4923 + isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
4924 + esc_attr($disabled)
4925 + );
4926 + echo '<button type="button" id="toggleSecretKeyVisibility">Show</button>';
4927 + echo '<p class="description">Secret key for validating Slack requests. Keep this secure.</p>';
4928 +
4929 + if (!$this->is_activated) {
4930 + echo '<div class="pro-feature-overlay">';
4931 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4932 + echo '</div>';
4933 + }
4934 + echo '</div>';
4935 +}
4936 +
4937 +public function mxchat_live_agent_bot_token_callback() {
4938 + $disabled = $this->is_activated ? '' : 'disabled';
4939 + $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4940 +
4941 + echo '<div class="' . esc_attr($class) . '">';
4942 + printf(
4943 + '<input type="password" id="live_agent_bot_token" name="mxchat_options[live_agent_bot_token]" value="%s" class="regular-text" %s />',
4944 + isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
4945 + esc_attr($disabled)
4946 + );
4947 + echo '<button type="button" id="toggleBotTokenVisibility">Show</button>';
4948 + echo '<p class="description">Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.</p>';
4949 +
4950 + if (!$this->is_activated) {
4951 + echo '<div class="pro-feature-overlay">';
4952 + echo '<a href="https://mxchat.ai/" target="_blank"><img src="' . plugin_dir_url(__FILE__) . '../images/pro-only-dark.png" alt="Pro Only" /></a>';
4953 + echo '</div>';
4954 + }
4955 + echo '</div>';
4956 +}
4957 +
4958 +public function mxchat_enqueue_admin_assets() {
4959 + wp_enqueue_style('wp-color-picker');
4960 +
4961 + // Get the plugin version or file modification time for cache busting
4962 + $plugin_version = '1.6.0'; // Replace this with your plugin's version
4963 +
4964 + // File paths
4965 + $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
4966 + $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
4967 + $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
4968 + $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
4969 +
4970 + // Check if files exist and get modification times
4971 + $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
4972 + $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
4973 + $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
4974 + $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
4975 +
4976 + // Enqueue scripts and styles with corrected paths
4977 + wp_enqueue_script(
4978 + 'mxchat-color-picker',
4979 + plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
4980 + array('wp-color-picker'),
4981 + $color_picker_version,
4982 + true
4983 + );
4984 +
4985 + wp_enqueue_script(
4986 + 'mxchat-embedding-check',
4987 + plugin_dir_url(__FILE__) . '../js/embedding-check.js',
4988 + array(),
4989 + $embedding_check_version,
4990 + true
4991 + );
4992 +
4993 + wp_enqueue_script(
4994 + 'mxchat-transcripts-js',
4995 + plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
4996 + array('jquery'),
4997 + $transcripts_js_version,
4998 + true
4999 + );
5000 +
5001 + wp_enqueue_script(
5002 + 'mxchat-admin-js',
5003 + plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5004 + array('jquery'),
5005 + $plugin_version,
5006 + true
5007 + );
5008 +
5009 +
5010 +wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
5011 + 'ajax_url' => admin_url('admin-ajax.php'),
5012 + 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5013 + 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5014 + 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5015 +));
5016 + // Enqueue the admin CSS
5017 + wp_enqueue_style(
5018 + 'mxchat-admin-css',
5019 + plugin_dir_url(__FILE__) . '../css/admin-style.css',
5020 + array(),
5021 + $admin_css_version
5022 + );
5023 +
5024 + // Localize the script for color picker and settings
5025 + wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5026 + 'ajax_url' => admin_url('admin-ajax.php'),
5027 + 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5028 + 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5029 + 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5030 + 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5031 + 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5032 + 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5033 + 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5034 + 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5035 + 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5036 + 'icon_color' => $this->options['icon_color'] ?? '#fff',
5037 + 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5038 + 'pre_chat_message' => $this->options['pre_chat_message'] ?? 'Hey there! Ask me anything!',
5039 + 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
5040 + 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5041 + 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5042 + 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? 'Would you like to join our mailing list? Please provide your email below.',
5043 + 'email_capture_response' => $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.',
5044 + 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? "Please provide the URL to the PDF you'd like to discuss.",
5045 + 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? "I've processed the PDF. What questions do you have about it?",
5046 + 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? "Sorry, I couldn't process the PDF. Please ensure it's a valid file.",
5047 + 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5048 + 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5049 + 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5050 + 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5051 + 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5052 + 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5053 + 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5054 + 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5055 + 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5056 + 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5057 + ));
5058 +}
5059 +
5060 + public function mxchat_sanitize($input) {
5061 + $new_input = array();
5062 +
5063 + if (isset($input['api_key'])) {
5064 + $new_input['api_key'] = sanitize_text_field($input['api_key']);
5065 + }
5066 +
5067 + if (isset($input['similarity_threshold'])) {
5068 + $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5069 + $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
5070 + }
5071 +
5072 + if (isset($input['xai_api_key'])) {
5073 + $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
5074 + }
5075 +
5076 + if (isset($input['claude_api_key'])) {
5077 + $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5078 + }
5079 +
5080 + if (isset($input['enable_woocommerce_integration'])) {
5081 + $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5082 + }
5083 +
5084 + if (isset($input['privacy_toggle'])) {
5085 + $new_input['privacy_toggle'] = $input['privacy_toggle'];
5086 + }
5087 +
5088 + if (isset($input['complianz_toggle'])) {
5089 + $new_input['complianz_toggle'] = $input['complianz_toggle'];
5090 + }
5091 +
5092 + // Handle custom privacy text input
5093 + if (isset($input['privacy_text'])) {
5094 + // Allow basic HTML for links
5095 + $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
5096 + }
5097 +
5098 + if (isset($input['system_prompt_instructions'])) {
5099 + $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
5100 + }
5101 +
5102 + if (isset($input['mxchat_pro_email'])) {
5103 + $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
5104 + }
5105 +
5106 + if (isset($input['mxchat_activation_key'])) {
5107 + $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
5108 + }
5109 +
5110 + if (isset($input['append_to_body'])) {
5111 + $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5112 + }
5113 +
5114 + if (isset($input['top_bar_title'])) {
5115 + $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5116 + }
5117 +
5118 + if (isset($input['enable_email_block'])) {
5119 + $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5120 + }
5121 +
5122 + if (isset($input['email_blocker_header_content'])) {
5123 + // wp_kses_post() allows standard HTML tags permitted by WordPress
5124 + $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
5125 + }
5126 + if (isset($input['email_blocker_button_text'])) {
5127 + $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5128 + }
5129 +
5130 + if (isset($input['intro_message'])) {
5131 + $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
5132 + }
5133 +
5134 + if (isset($input['input_copy'])) {
5135 + $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
5136 + }
5137 +
5138 +
5139 + if (isset($input['rate_limit_message'])) {
5140 + $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5141 + }
5142 +
5143 + if (isset($input['rate_limit_logged_in'])) {
5144 + $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5145 + $rate_limit = sanitize_text_field($input['rate_limit_logged_in']);
5146 +
5147 + if (in_array($rate_limit, $allowed_limits, true)) {
5148 + $new_input['rate_limit_logged_in'] = $rate_limit;
5149 + } else {
5150 + $new_input['rate_limit_logged_in'] = '100'; // Default for logged-in users
5151 + }
5152 + }
5153 +
5154 + if (isset($input['rate_limit_logged_out'])) {
5155 + $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5156 + $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
5157 +
5158 + if (in_array($rate_limit, $allowed_limits, true)) {
5159 + $new_input['rate_limit_logged_out'] = $rate_limit;
5160 + } else {
5161 + $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
5162 + }
5163 + }
5164 +
5165 + if (isset($input['pre_chat_message'])) {
5166 + $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5167 + }
5168 +
5169 + if (isset($input['model'])) {
5170 + $allowed_models = array(
5171 + 'grok-beta',
5172 + 'grok-2',
5173 + 'claude-3-5-sonnet-20241022',
5174 + 'claude-3-opus-20240229',
5175 + 'claude-3-sonnet-20240229',
5176 + 'claude-3-haiku-20240307',
5177 + 'gpt-4o',
5178 + 'gpt-4o-mini',
5179 + 'gpt-4-turbo',
5180 + 'gpt-4',
5181 + 'gpt-3.5-turbo',
5182 + );
5183 + if (in_array($input['model'], $allowed_models)) {
5184 + $new_input['model'] = sanitize_text_field($input['model']);
5185 + }
5186 + }
5187 +
5188 + // Sanitize new pro features
5189 + if (isset($input['close_button_color'])) {
5190 + $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5191 + }
5192 +
5193 + if (isset($input['chatbot_bg_color'])) {
5194 + $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
5195 + }
5196 +
5197 + if (isset($input['woocommerce_consumer_key'])) {
5198 + $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
5199 + }
5200 +
5201 + if (isset($input['woocommerce_consumer_secret'])) {
5202 + $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
5203 + }
5204 +
5205 + if (isset($input['user_message_bg_color'])) {
5206 + $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
5207 + }
5208 +
5209 + if (isset($input['user_message_font_color'])) {
5210 + $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
5211 + }
5212 +
5213 + if (isset($input['bot_message_bg_color'])) {
5214 + $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
5215 + }
5216 +
5217 + if (isset($input['bot_message_font_color'])) {
5218 + $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
5219 + }
5220 +
5221 + if (isset($input['live_agent_message_bg_color'])) {
5222 + $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
5223 + }
5224 +
5225 + if (isset($input['live_agent_message_font_color'])) {
5226 + $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
5227 + }
5228 +
5229 + if (isset($input['mode_indicator_bg_color'])) {
5230 + $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
5231 + }
5232 +
5233 + if (isset($input['mode_indicator_font_color'])) {
5234 + $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
5235 + }
5236 +
5237 + if (isset($input['toolbar_icon_color'])) {
5238 + $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
5239 + }
5240 +
5241 + if (isset($input['top_bar_bg_color'])) {
5242 + $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5243 + }
5244 +
5245 + if (isset($input['send_button_font_color'])) {
5246 + $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5247 + }
5248 +
5249 + if (isset($input['chatbot_background_color'])) {
5250 + $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
5251 + }
5252 +
5253 + if (isset($input['icon_color'])) {
5254 + $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
5255 + }
5256 +
5257 + if (isset($input['custom_icon'])) {
5258 + $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
5259 + }
5260 +
5261 +
5262 + if (isset($input['title_icon'])) {
5263 + $new_input['title_icon'] = esc_url_raw($input['title_icon']);
5264 + }
5265 +
5266 + if (isset($input['chat_input_font_color'])) {
5267 + $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
5268 + }
5269 +
5270 + // Sanitize link_target_toggle
5271 + if (isset($input['link_target_toggle'])) {
5272 + $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
5273 + }
5274 +
5275 + // Sanitize Loops API Key
5276 + if (isset($input['loops_api_key'])) {
5277 + $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
5278 + }
5279 +
5280 + if (isset($input['chat_persistence_toggle'])) {
5281 + $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
5282 + }
5283 +
5284 +
5285 +
5286 + if (isset($input['popular_question_1'])) {
5287 + $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
5288 + }
5289 +
5290 + if (isset($input['popular_question_2'])) {
5291 + $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
5292 + }
5293 +
5294 + if (isset($input['popular_question_3'])) {
5295 + $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
5296 + }
5297 +
5298 + if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
5299 + $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
5300 + }
5301 +
5302 + // Sanitize Loops Mailing List
5303 + if (isset($input['loops_mailing_list'])) {
5304 + $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5305 + }
5306 +
5307 + // Sanitize Triggered Phrase Response
5308 + if (isset($input['triggered_phrase_response'])) {
5309 + $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5310 + }
5311 +
5312 + if (isset($input['email_capture_response'])) {
5313 + $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
5314 + }
5315 +
5316 +
5317 + // Sanitize Brave Search Settings
5318 + if (isset($input['brave_api_key'])) {
5319 + $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
5320 + }
5321 +
5322 + if (isset($input['brave_image_count'])) {
5323 + $image_count = intval($input['brave_image_count']);
5324 + $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
5325 + }
5326 +
5327 + if (isset($input['brave_safe_search'])) {
5328 + $allowed = array('strict', 'off');
5329 + $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
5330 + }
5331 +
5332 + if (isset($input['brave_news_count'])) {
5333 + $news_count = intval($input['brave_news_count']);
5334 + $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
5335 + }
5336 +
5337 + if (isset($input['brave_country'])) {
5338 + $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
5339 + }
5340 +
5341 + if (isset($input['brave_language'])) {
5342 + $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
5343 + }
5344 +
5345 +
5346 + if (isset($input['chat_toolbar_toggle'])) {
5347 + $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5348 + }
5349 +
5350 +
5351 + if (isset($input['pdf_intent_trigger_text'])) {
5352 + $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5353 + }
5354 +
5355 + if (isset($input['pdf_intent_success_text'])) {
5356 + $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
5357 + }
5358 +
5359 + if (isset($input['pdf_intent_error_text'])) {
5360 + $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
5361 + }
5362 +
5363 + if (isset($input['pdf_max_pages'])) {
5364 + $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
5365 + if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
5366 + $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
5367 + }
5368 + }
5369 +
5370 +
5371 + if (isset($input['live_agent_webhook_url'])) {
5372 + $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
5373 + }
5374 + if (isset($input['live_agent_secret_key'])) {
5375 + $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
5376 + }
5377 +
5378 + // Live Agent Integration
5379 + if (isset($input['live_agent_bot_token'])) {
5380 + $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
5381 + }
5382 +
5383 + if (isset($input['live_agent_status'])) {
5384 + $new_input['live_agent_status'] = ($input['live_agent_status'] === 'online') ? 'online' : 'offline';
5385 + }
5386 + if (isset($input['live_agent_away_message'])) {
5387 + $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
5388 + }
5389 + if (isset($input['live_agent_notification_message'])) {
5390 + $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
5391 + }
5392 +
5393 +
5394 + return $new_input;
5395 + }
5396 +
5397 +
5398 + // Method to append the chatbot to the body
5399 + public function mxchat_append_chatbot_to_body() {
5400 + $options = get_option('mxchat_options');
5401 + if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
5402 + echo do_shortcode('[mxchat_chatbot floating="yes"]');
5403 + }
5404 + }
5405 +
5406 +
5407 +
5408 +private static function mxchat_extract_main_content($html) {
5409 + $dom = new DOMDocument;
5410 + libxml_use_internal_errors(true); // Suppress HTML parsing errors
5411 + @$dom->loadHTML($html);
5412 + libxml_clear_errors();
5413 +
5414 + $xpath = new DOMXPath($dom);
5415 +
5416 + // Simplified selectors focusing on common content areas
5417 + $selectors = [
5418 + '//article',
5419 + '//*[@id="content"]',
5420 + '//*[@class="entry-content"]',
5421 + '//main',
5422 + ];
5423 +
5424 + foreach ($selectors as $selector) {
5425 + $nodes = $xpath->query($selector);
5426 + if ($nodes->length > 0) {
5427 + $content = '';
5428 + foreach ($nodes as $node) {
5429 + $content .= $dom->saveHTML($node);
5430 + }
5431 + return $content;
5432 + }
5433 + }
5434 +
5435 + // Fallback: Return the entire body content if no specific selector matches
5436 + $body = $dom->getElementsByTagName('body');
5437 + return $body->length > 0 ? $dom->saveHTML($body->item(0)) : $html;
5438 +}
5439 +private function mxchat_fetch_loops_mailing_lists($api_key) {
5440 + $url = 'https://app.loops.so/api/v1/lists';
5441 + $response = wp_remote_get($url, array(
5442 + 'headers' => array(
5443 + 'Authorization' => 'Bearer ' . $api_key,
5444 + 'Content-Type' => 'application/json'
5445 + )
5446 + ));
5447 +
5448 + if (is_wp_error($response)) {
5449 + return array();
5450 + }
5451 +
5452 + $body = wp_remote_retrieve_body($response);
5453 + $lists = json_decode($body, true);
5454 +
5455 + return isset($lists) && is_array($lists) ? $lists : array();
5456 +}
5457 +
5458 +
5459 +
5460 +
5461 +
5462 +function mxchat_calculate_cosine_similarity($vec1, $vec2) {
5463 + if (empty($vec1) || empty($vec2)) {
5464 + return 0.0;
5465 + }
5466 +
5467 + $dot_product = 0.0;
5468 + $norm_a = 0.0;
5469 + $norm_b = 0.0;
5470 +
5471 + for ($i = 0; $i < count($vec1); $i++) {
5472 + $dot_product += $vec1[$i] * $vec2[$i];
5473 + $norm_a += pow($vec1[$i], 2);
5474 + $norm_b += pow($vec2[$i], 2);
5475 + }
5476 +
5477 + if ($norm_a == 0.0 || $norm_b == 0.0) {
5478 + return 0.0;
5479 + } else {
5480 + return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
5481 + }
5482 +}
5483 +
5484 +
5485 +
5486 +
5487 +}
5488 +?>