PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.3.9
MxChat – AI Chatbot & Content Generation for WordPress v2.3.9
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
← All changes | includes/class-mxchat-admin.php +4525 -3167 2.0.32.3.9 View file →
@@ -6,13 +6,15 @@
6 6 class MxChat_Admin {
7 7 private $options;
8 8 private $chat_count;
9 9 private $is_activated;
10 + private $knowledge_manager;
10 11
11 - public function __construct() {
12 + public function __construct($knowledge_manager = null) {
12 13 $this->options = get_option('mxchat_options');
13 14 $this->chat_count = get_option('mxchat_chat_count', 0);
14 15 $this->is_activated = $this->is_license_active();
16 + $this->knowledge_manager = $knowledge_manager;
15 17
16 18 // Initialize default options if they are not set
17 19 if (!$this->options) {
18 20 $this->initialize_default_options();
@@ -23,49 +25,40 @@
23 25 add_action('admin_init', array($this, 'mxchat_page_init'));
24 26 add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
25 27 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
26 28 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 29 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
29 30 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
30 31 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 32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 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 35 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 36 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
39 37 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
40 38 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_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
50 - if (isset($this->options['enable_woocommerce_integration']) &&
51 - ($this->options['enable_woocommerce_integration'] === '1' ||
52 - $this->options['enable_woocommerce_integration'] === 'on')) {
39 + add_action('admin_post_mxchat_edit_intent', array($this, 'mxchat_handle_edit_intent'));
40 + add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
41 + add_action('admin_init', array($this, 'mxchat_transcripts_page_init'));
42 + add_action('wp_ajax_dismiss_live_agent_notice', array($this, 'dismiss_live_agent_notice'));
53 43
54 - //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
55 - add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
56 - add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
57 - add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
58 - }
44 + add_action('admin_init', array($this, 'register_pinecone_settings'));
45 +
46 + add_action('admin_notices', array($this, 'display_admin_notices'));
47 +
48 + add_action('wp_ajax_mxchat_test_streaming_actual', [$this, 'mxchat_handle_test_streaming_actual']);
49 + add_action('wp_ajax_mxchat_test_streaming', [$this, 'mxchat_handle_test_streaming']); // Keep existing as fallback
50 +
59 51 }
60 52
61 - // Method to check if the license is active
62 53 private function is_license_active() {
63 - $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
64 - return $license_status === esc_html__('active', 'mxchat');
65 - }
54 + // Get the raw value without translation
55 + $license_status = get_option('mxchat_license_status', 'inactive');
66 56
67 - // Initialize default options
57 + // Check against multiple possible values, bypassing translation issues
58 + return ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
59 +}
60 +
68 61 private function initialize_default_options() {
69 62 $default_options = array(
70 63 'api_key' => '',
71 64 'xai_api_key' => '',
@@ -70,25 +63,46 @@
70 63 'api_key' => '',
71 64 'xai_api_key' => '',
72 65 'claude_api_key' => '',
73 66 'deepseek_api_key' => '',
74 - '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:
75 - - Your name is [Chatbot Name].
76 - - 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.
77 - - 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.
78 - - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
79 - - 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'),
67 + 'voyage_api_key' => '',
68 + 'gemini_api_key' => '',
69 + 'enable_streaming_toggle' => 'on',
70 + 'embedding_model' => 'text-embedding-ada-002',
71 + 'system_prompt_instructions' => 'You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
72 +
73 + # Response Style - CRITICALLY IMPORTANT
74 + - MAXIMUM LENGTH: 1-3 short sentences per response
75 + - Ultra-concise: Get straight to the answer with no filler
76 + - No introductions like "Sure!" or "I\'d be happy to help"
77 + - No phrases like "based on my knowledge" or "according to information"
78 + - No explanatory text before giving the answer
79 + - No summaries or repetition
80 + - Hyperlink all URLs
81 + - Respond in user\'s language
82 + - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
83 +
84 + # Knowledge Base Requirements - PREVENT HALLUCINATIONS
85 + - ONLY answer questions using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
86 + - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
87 + - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
88 + - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
89 + - Better to admit insufficient information than provide inaccurate answers',
80 90 'model' => esc_html__('gpt-4o', 'mxchat'),
81 91 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
82 92 'role_rate_limits' => array(),
83 93 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
84 94 'enable_email_block' => '',
85 - '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'),
95 + '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>", 'mxchat'),
86 96 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
97 + 'enable_name_field' => 'off', // NEW
98 + 'name_field_placeholder' => esc_html__('Enter your name', 'mxchat'), // NEW
87 99 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
88 - 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
100 + 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
101 + 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
89 102 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
90 103 'append_to_body' => esc_html__('off', 'mxchat'),
104 + 'contextual_awareness_toggle' => 'off',
91 105 'close_button_color' => esc_html__('#fff', 'mxchat'),
92 106 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
93 107 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
94 108 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
@@ -105,17 +119,19 @@
105 119
106 120 // New fields for Loops Integration
107 121 'loops_api_key' => '',
108 122 'loops_mailing_list' => '',
109 - 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
110 - 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
123 + 'triggered_phrase_response' => __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
124 + 'email_capture_response' => __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
111 125 'popular_question_1' => '',
112 126 'popular_question_2' => '',
113 127 'popular_question_3' => '',
114 - 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
115 - 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
116 - 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
128 + 'pdf_intent_trigger_text' => __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
129 + 'pdf_intent_success_text' => __("I've processed the PDF. What questions do you have about it?", 'mxchat'),
130 + 'pdf_intent_error_text' => __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
117 131 'pdf_max_pages' => 69,
132 + 'show_pdf_upload_button' => 'on',
133 + 'show_word_upload_button' => 'on',
118 134
119 135 // Live Agent Integration
120 136 'live_agent_webhook_url' => '',
121 137 'live_agent_secret_key' => '',
@@ -149,10 +165,8 @@
149 165 // Update the $this->options property
150 166 $this->options = $merged_options;
151 167 }
152 168
153 -
154 -
155 169 public function mxchat_add_plugin_page() {
156 170 // Main menu page
157 171 add_menu_page(
158 172 esc_html__('MxChat Settings', 'mxchat'),
@@ -173,26 +187,24 @@
173 187 'mxchat-prompts',
174 188 array($this, 'mxchat_create_prompts_page')
175 189 );
176 190
177 - // Submenu page for Chat Transcripts
178 191 add_submenu_page(
179 - 'mxchat-max', // Corrected parent slug to match the main menu
192 + 'mxchat-max',
180 193 esc_html__('Chat Transcripts', 'mxchat'),
181 194 esc_html__('Transcripts', 'mxchat'),
182 195 'manage_options',
183 196 'mxchat-transcripts',
184 - array($this, 'mxchat_create_transcripts_page') // Prefixed function name with mxchat_
197 + array($this, 'mxchat_create_transcripts_page')
185 198 );
186 199
187 - // Submenu page for Intents
188 200 add_submenu_page(
189 - 'mxchat-max', // Parent slug
190 - esc_html__('MxChat Intents', 'mxchat'), // Page title
191 - esc_html__('Intents', 'mxchat'), // Menu title
192 - 'manage_options', // Capability
193 - 'mxchat-intents', // Menu slug
194 - array($this, 'mxchat_intents_page_html') // Callback function
201 + 'mxchat-max',
202 + esc_html__('MxChat Actions', 'mxchat'),
203 + esc_html__('Actions', 'mxchat'),
204 + 'manage_options',
205 + 'mxchat-actions',
206 + array($this, 'mxchat_actions_page_html')
195 207 );
196 208
197 209 add_submenu_page(
198 210 'mxchat-max',
@@ -219,156 +231,299 @@
219 231 $addons_page = new MxChat_Addons();
220 232 $addons_page->render_page();
221 233 }
222 234
223 -public function mxchat_save_setting_callback() {
224 - check_ajax_referer('mxchat_save_setting_nonce');
225 - if (!current_user_can('manage_options')) {
226 - wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
235 +/**
236 + * Test actual streaming functionality in the WordPress environment
237 + */
238 +public function mxchat_handle_test_streaming_actual() {
239 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
240 +
241 + // Check if headers have already been sent
242 + if (headers_sent()) {
243 + wp_send_json_error(['message' => 'Headers already sent - streaming not possible']);
244 + return;
227 245 }
228 - $name = isset($_POST['name']) ? $_POST['name'] : '';
229 - // Strip slashes from the value before saving
230 - $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
231 - if (empty($name)) {
232 - wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
246 +
247 + // Check for required functions
248 + if (!function_exists('curl_init')) {
249 + wp_send_json_error(['message' => 'cURL not available - streaming requires cURL']);
250 + return;
233 251 }
234 - // Load the full options array
252 +
253 + // Get user's selected model and API key
235 254 $options = get_option('mxchat_options', []);
236 - // Handle special cases
237 - switch ($name) {
238 - case 'additional_popular_questions':
239 - $questions = json_decode($value, true); // No need for stripslashes here
240 - if (is_array($questions)) {
241 - $options[$name] = $questions;
242 - // Also update old option for backwards compatibility
243 - update_option('additional_popular_questions', $questions);
244 - }
255 + $selected_model = $options['model'] ?? 'gpt-4o';
256 +
257 + // Get the provider from the model
258 + $model_parts = explode('-', $selected_model);
259 + $provider = strtolower($model_parts[0]);
260 +
261 + // Get the appropriate API key
262 + $api_key = '';
263 + switch ($provider) {
264 + case 'gpt':
265 + case 'o1':
266 + $api_key = $options['api_key'] ?? '';
245 267 break;
246 - case 'user_message_bg_color':
247 - case 'user_message_font_color':
248 - case 'bot_message_bg_color':
249 - case 'bot_message_font_color':
250 - case 'top_bar_bg_color':
251 - case 'send_button_font_color':
252 - case 'chatbot_background_color':
253 - case 'icon_color':
254 - case 'chat_input_font_color':
255 - case 'live_agent_message_bg_color':
256 - case 'live_agent_message_font_color':
257 - case 'mode_indicator_bg_color':
258 - case 'mode_indicator_font_color':
259 - case 'toolbar_icon_color':
260 - // Store color values directly
261 - $options[$name] = $value;
268 + case 'claude':
269 + $api_key = $options['claude_api_key'] ?? '';
262 270 break;
263 - case 'live_agent_status':
264 - // Set the new value
265 - $options[$name] = ($value === 'on') ? 'on' : 'off';
271 + case 'grok':
272 + $api_key = $options['xai_api_key'] ?? '';
266 273 break;
267 - case 'enable_woocommerce_integration':
268 - // Handle values that used to be 1/0
269 - $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
274 + case 'deepseek':
275 + $api_key = $options['deepseek_api_key'] ?? '';
270 276 break;
277 + case 'gemini':
278 + $api_key = $options['gemini_api_key'] ?? '';
279 + break;
271 280 default:
272 - // Handle role rate limits
273 - if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
274 - // Extract role ID from the name
275 - preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
276 - if (isset($matches[1])) {
277 - $role_id = $matches[1];
278 - // Initialize role_rate_limits if it doesn't exist
279 - if (!isset($options['role_rate_limits'])) {
280 - $options['role_rate_limits'] = [];
281 - }
282 - // Update the specific role's rate limit
283 - $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
284 - break;
285 - }
286 - }
287 - // Handle toggles
288 - if (strpos($name, 'toggle') !== false || in_array($name, [
289 - 'chat_persistence_toggle',
290 - 'privacy_toggle',
291 - 'complianz_toggle',
292 - 'chat_toolbar_toggle' // Removed live_agent_status from here
293 - ])) {
294 - $options[$name] = ($value === 'on') ? 'on' : 'off';
295 - } else {
296 - // Store all other values directly
297 - $options[$name] = $value;
298 - }
281 + // Default to OpenAI for unknown models
282 + $api_key = $options['api_key'] ?? '';
283 + $provider = 'gpt';
299 284 break;
300 285 }
301 - // Save all updates
302 - $updated = update_option('mxchat_options', $options);
303 - // Handle backwards compatibility for certain fields
304 - $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
305 - 'brave_country', 'brave_language', 'similarity_threshold'];
306 - if (in_array($name, $legacy_fields)) {
307 - // Also update the individual option for backwards compatibility
308 - update_option($name, $value);
286 +
287 + if (empty($api_key)) {
288 + wp_send_json_error(['message' => "API key not configured for {$provider} provider"]);
289 + return;
309 290 }
310 - if ($updated) {
311 - wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
312 - } else {
313 - wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
291 +
292 + // Test streaming with the selected model and provider
293 + try {
294 + $this->perform_streaming_test($provider, $selected_model, $api_key);
295 + } catch (Exception $e) {
296 + wp_send_json_error(['message' => 'Streaming test exception: ' . $e->getMessage()]);
314 297 }
315 298 }
316 299
317 300 /**
318 - * Handles AJAX auto-save for prompts and auto-sync settings.
301 + * Perform the actual streaming test
319 302 */
320 -public function mxchat_save_prompts_setting_callback() {
321 - // Check the correct nonce for this action.
322 - check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
303 +private function perform_streaming_test($provider, $model, $api_key) {
304 + // Set streaming headers
305 + header('Content-Type: text/event-stream');
306 + header('Cache-Control: no-cache');
307 + header('X-Accel-Buffering: no'); // Disable nginx buffering
323 308
324 - if ( ! current_user_can( 'manage_options' ) ) {
325 - wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
309 + // Prepare test message
310 + $test_message = "Please respond with exactly: 'Streaming test successful!' - send this as a short response for testing.";
311 +
312 + // Configure API request based on provider
313 + $url = '';
314 + $headers = [];
315 + $body = [];
316 +
317 + switch ($provider) {
318 + case 'gpt':
319 + case 'o1':
320 + $url = 'https://api.openai.com/v1/chat/completions';
321 + $headers = [
322 + 'Content-Type: application/json',
323 + 'Authorization: Bearer ' . $api_key
324 + ];
325 + $body = [
326 + 'model' => $model,
327 + 'messages' => [['role' => 'user', 'content' => $test_message]],
328 + 'max_tokens' => 50,
329 + 'temperature' => 0.3,
330 + 'stream' => true
331 + ];
332 + break;
333 +
334 + case 'claude':
335 + $url = 'https://api.anthropic.com/v1/messages';
336 + $headers = [
337 + 'Content-Type: application/json',
338 + 'x-api-key: ' . $api_key,
339 + 'anthropic-version: 2023-06-01'
340 + ];
341 + $body = [
342 + 'model' => $model,
343 + 'messages' => [['role' => 'user', 'content' => $test_message]],
344 + 'max_tokens' => 50,
345 + 'temperature' => 0.3,
346 + 'stream' => true
347 + ];
348 + break;
349 +
350 + case 'grok':
351 + $url = 'https://api.x.ai/v1/chat/completions';
352 + $headers = [
353 + 'Content-Type: application/json',
354 + 'Authorization: Bearer ' . $api_key
355 + ];
356 + $body = [
357 + 'model' => $model,
358 + 'messages' => [['role' => 'user', 'content' => $test_message]],
359 + 'max_tokens' => 50,
360 + 'temperature' => 0.3,
361 + 'stream' => true
362 + ];
363 + break;
364 +
365 + case 'deepseek':
366 + $url = 'https://api.deepseek.com/v1/chat/completions';
367 + $headers = [
368 + 'Content-Type: application/json',
369 + 'Authorization: Bearer ' . $api_key
370 + ];
371 + $body = [
372 + 'model' => $model,
373 + 'messages' => [['role' => 'user', 'content' => $test_message]],
374 + 'max_tokens' => 50,
375 + 'temperature' => 0.3,
376 + 'stream' => true
377 + ];
378 + break;
379 +
380 + default:
381 + echo "data: " . json_encode(['error' => 'Unsupported provider for streaming test: ' . $provider]) . "\n\n";
382 + flush();
383 + return;
326 384 }
327 385
328 - $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
329 - $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
386 + // Initialize cURL
387 + $ch = curl_init();
388 + curl_setopt($ch, CURLOPT_URL, $url);
389 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
390 + curl_setopt($ch, CURLOPT_POST, true);
391 + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
392 + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
393 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
394 + curl_setopt($ch, CURLOPT_TIMEOUT, 30);
395 + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use ($provider) {
396 + return $this->process_streaming_test_data($data, $provider);
397 + });
330 398
331 - if ( empty( $name ) ) {
332 - wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
399 + // Send initial test message
400 + echo "data: " . json_encode(['content' => '[Starting streaming test...]']) . "\n\n";
401 + flush();
402 +
403 + $result = curl_exec($ch);
404 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
405 + $curl_error = curl_error($ch);
406 + curl_close($ch);
407 +
408 + if ($curl_error) {
409 + echo "data: " . json_encode(['error' => 'cURL Error: ' . $curl_error]) . "\n\n";
410 + flush();
411 + return;
333 412 }
334 413
335 - // Handle standalone auto-sync settings.
336 - if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
337 - // Convert checkbox value to "1" if checked, "0" if not.
338 - $value = ( $value === 'on' ) ? '1' : '0';
339 - update_option( $name, $value );
340 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
414 + if ($http_code !== 200) {
415 + echo "data: " . json_encode(['error' => 'API returned HTTP ' . $http_code]) . "\n\n";
416 + flush();
417 + return;
341 418 }
342 419
343 - // Handle fields stored in the 'mxchat_prompts_options' array.
344 -// Handle fields stored in the 'mxchat_prompts_options' array.
345 -if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
346 - // Extract the key name using regex.
347 - if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
348 - $key = sanitize_text_field( $matches[1] );
349 - $options = get_option( 'mxchat_prompts_options', [] );
420 + // Send completion signal
421 + echo "data: [DONE]\n\n";
422 + flush();
423 +}
350 424
351 - // Remove Pinecone-specific settings from the main plugin handling.
352 - if ( 'mxchat_use_pinecone' === $key ) {
353 - // Optionally, you could simply ignore this field or set a default value.
354 - // For example, ensure it's always 0 or remove it entirely:
355 - // $options[ $key ] = '0';
356 - // Or, if you prefer, just return an error so that the main plugin doesn't handle it:
357 - wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
425 +/**
426 + * Process streaming data for the test
427 + */
428 +private function process_streaming_test_data($data, $provider) {
429 + static $chunk_count = 0;
430 +
431 + $lines = explode("\n", $data);
432 +
433 + foreach ($lines as $line) {
434 + if (trim($line) === '') {
435 + continue;
436 + }
437 +
438 + // Handle different provider formats
439 + if ($provider === 'claude') {
440 + // Claude uses event: and data: format
441 + if (strpos($line, 'data: ') === 0) {
442 + $json_str = substr($line, 6);
443 + $json = json_decode($json_str, true);
444 +
445 + if (isset($json['type']) && $json['type'] === 'content_block_delta') {
446 + if (isset($json['delta']['text'])) {
447 + $chunk_count++;
448 + echo "data: " . json_encode([
449 + 'content' => $json['delta']['text'],
450 + 'test_chunk' => $chunk_count
451 + ]) . "\n\n";
452 + flush();
453 + }
454 + }
455 + }
358 456 } else {
359 - // For all other fields, use the existing logic.
360 - $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
457 + // OpenAI, X.AI, DeepSeek format
458 + if (strpos($line, 'data: ') === 0) {
459 + $json_str = substr($line, 6);
460 +
461 + if ($json_str === '[DONE]') {
462 + // Don't echo [DONE] here, let the main function handle it
463 + continue;
464 + }
465 +
466 + $json = json_decode($json_str, true);
467 + if (isset($json['choices'][0]['delta']['content'])) {
468 + $chunk_count++;
469 + echo "data: " . json_encode([
470 + 'content' => $json['choices'][0]['delta']['content'],
471 + 'test_chunk' => $chunk_count
472 + ]) . "\n\n";
473 + flush();
474 + }
475 + }
361 476 }
362 - update_option( 'mxchat_prompts_options', $options );
363 - wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
364 477 }
478 +
479 + return strlen($data);
365 480 }
366 481
482 +/**
483 + * Updated version of your existing test method (keep this as a fallback)
484 + */
485 +public function mxchat_handle_test_streaming() {
486 + check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
367 487
368 - wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
488 + // Use the actual streaming test instead
489 + $this->mxchat_handle_test_streaming_actual();
369 490 }
370 491
492 +
493 +public function register_pinecone_settings() {
494 + register_setting(
495 + 'mxchat_pinecone_addon_options',
496 + 'mxchat_pinecone_addon_options',
497 + array(
498 + 'type' => 'array',
499 + 'sanitize_callback' => array($this, 'sanitize_pinecone_settings'),
500 + 'default' => array(
501 + 'mxchat_use_pinecone' => '0',
502 + 'mxchat_pinecone_api_key' => '',
503 + 'mxchat_pinecone_host' => '',
504 + 'mxchat_pinecone_index' => '',
505 + 'mxchat_pinecone_environment' => ''
506 + )
507 + )
508 + );
509 +}
510 +
511 +public function sanitize_pinecone_settings($input) {
512 + $sanitized = array();
513 +
514 + $sanitized['mxchat_use_pinecone'] = isset($input['mxchat_use_pinecone']) ? '1' : '0';
515 + $sanitized['mxchat_pinecone_api_key'] = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
516 + $sanitized['mxchat_pinecone_host'] = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
517 + $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
518 + $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
519 +
520 + // Remove https:// from host if present
521 + $sanitized['mxchat_pinecone_host'] = str_replace(['https://', 'http://'], '', $sanitized['mxchat_pinecone_host']);
522 +
523 + return $sanitized;
524 +}
525 +
371 526 public function mxchat_display_admin_notice() {
372 527 // Success notice
373 528 if ($message = get_transient('mxchat_admin_notice_success')) {
374 529 ?>
@@ -391,2041 +546,2162 @@
391 546 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
392 547 }
393 548 }
394 549
550 +public function show_live_agent_disabled_banner() {
551 + $show_disabled_notice = get_option('mxchat_show_live_agent_disabled_notice', false);
395 552
396 -
397 -
398 -
553 + if ($show_disabled_notice) {
554 + ?>
555 + <div class="mxchat-live-agent-disabled-notice" id="mxchat-disabled-notice">
556 + <div class="mxchat-pro-notification">
557 + <button type="button" class="mxchat-dismiss-btn" onclick="dismissLiveAgentNotice()" aria-label="Dismiss notification">
558 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
559 + <line x1="18" y1="6" x2="6" y2="18"></line>
560 + <line x1="6" y1="6" x2="18" y2="18"></line>
561 + </svg>
562 + </button>
563 + <div class="mxchat-live-agent-content">
564 + <h3>🔧 Live Agent Integration Updated!</h3>
565 + <p>We've temporarily disabled your Live Agent integration due to recent enhancements that have made it much better! You can easily turn it back on by going to <strong>Toolbar & Components → Live Agent Settings</strong> and reviewing the new configuration options.</p>
566 + </div>
567 + </div>
568 + </div>
569 + <?php
570 + }
571 +}
399 572 public function mxchat_create_admin_page() {
573 + $this->add_live_agent_nonce();
400 574
401 575 ?>
402 - <div class="wrap mxchat-admin">
403 - <?php if (!$this->is_activated): ?>
404 - <div class="mxchat-pro-banner">
405 - <p>
406 - <?php echo esc_html__('For a limited time, get lifetime access and save $20 on MxChat Pro!', 'mxchat'); ?>
407 - <a href="https://mxchat.ai/" target="_blank"><?php echo esc_html__('Upgrade to Pro today', 'mxchat'); ?></a>
408 - </p>
576 + <div class="wrap mxchat-wrapper">
577 + <!-- Hero Section -->
578 + <div class="mxchat-hero">
579 + <h1 class="mxchat-main-title">
580 + <span class="mxchat-gradient-text">MxChat</span> Settings
581 + </h1>
582 + <p class="mxchat-hero-subtitle">
583 + <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
584 + </p>
409 585 </div>
410 - <?php endif; ?>
411 586
412 - <div class="mxchat-agents-banner">
413 - <p>
414 - <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'); ?>
415 - </p>
416 - </div>
587 + <div class="mxchat-content">
588 +
589 + <?php if (!$this->is_activated): ?>
590 + <div class="mxchat-pro-card">
591 + <div class="mxchat-pro-notification">
592 + <div class="mxchat-pro-content">
593 + <h3>🚀 Limited Lifetime Offer: Save 30% on MxChat Pro, Agency, or Agency Plus!</h3>
594 + <p>Unlock <strong>unlimited access</strong> to our growing collection of powerful add-ons including Admin AI Assistant (ChatGPT-like experience), Forms Builder, AI Theme Generator, WooCommerce, Perplexity, and more – all included with your <strong>lifetime license!</strong></p> </div>
595 + <div class="mxchat-pro-cta">
596 + <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade Today', 'mxchat'); ?></a>
597 + <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
598 + </div>
599 + </div>
600 + </div>
601 + <?php endif; ?>
417 602
418 - <h2 class="mxchat-nav-tab-wrapper">
419 - <a href="#chatbot" class="mxchat-nav-tab mxchat-nav-tab-active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></a>
420 - <a href="#embed" class="mxchat-nav-tab" data-tab="embed"><?php echo esc_html__('Integrations', 'mxchat'); ?></a>
421 - <a href="#general" class="mxchat-nav-tab" data-tab="general"><?php echo esc_html__('FAQ', 'mxchat'); ?></a>
422 - </h2>
603 + <?php
604 + $this->show_live_agent_disabled_banner();
605 + ?>
423 606
424 - <div id="chatbot" class="mxchat-tab-content active">
425 - <div class="mxchat-autosave-section">
426 - <?php do_settings_sections('mxchat-chatbot'); ?>
607 + <!-- Tabs Navigation -->
608 + <div class="mxchat-tabs">
609 + <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
610 + <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
611 + <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
427 612 </div>
428 - </div>
429 613
430 - <div id="embed" class="mxchat-tab-content">
431 - <div class="mxchat-autosave-section">
432 - <div class="mxchat-settings-section">
433 - <h2><?php echo esc_html__('WooCommerce Settings', 'mxchat'); ?></h2>
434 - <table class="form-table">
435 - <?php do_settings_fields('mxchat-embed', 'mxchat_woocommerce_section'); ?>
436 - </table>
614 + <!-- Tab Contents -->
615 + <div id="chatbot" class="mxchat-tab-content active">
616 + <div class="mxchat-card">
617 + <div class="mxchat-autosave-section">
618 + <?php do_settings_sections('mxchat-chatbot'); ?>
619 + </div>
437 620 </div>
438 - <div class="section-divider"></div>
621 + </div>
439 622
440 - <div class="mxchat-settings-section">
441 - <h2><?php echo esc_html__('Loops Settings', 'mxchat'); ?></h2>
442 - <table class="form-table">
443 - <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
444 - </table>
623 + <div id="embed" class="mxchat-tab-content">
624 +
625 + <div class="mxchat-card">
626 + <h2><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h2>
627 + <div class="mxchat-autosave-section">
628 + <table class="form-table">
629 + <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
630 + </table>
631 + </div>
445 632 </div>
446 633
447 - <div class="section-divider"></div>
448 634
449 - <div class="mxchat-settings-section">
450 - <h2><?php echo esc_html__('Brave Search Settings', 'mxchat'); ?></h2>
451 - <table class="form-table">
452 - <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
453 - </table>
635 + <div class="mxchat-card">
636 + <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
637 + <div class="mxchat-autosave-section">
638 + <table class="form-table">
639 + <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
640 + </table>
641 + </div>
454 642 </div>
455 643
456 - <div class="section-divider"></div>
644 + <div class="mxchat-card">
645 + <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
646 + <div class="mxchat-autosave-section">
647 + <table class="form-table">
648 + <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
649 + </table>
650 + </div>
651 + </div>
457 652
458 - <div class="mxchat-settings-section">
459 - <h2><?php echo esc_html__('Toolbar Settings & Intents', 'mxchat'); ?></h2>
460 - <table class="form-table">
461 - <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
462 - </table>
653 + <div class="mxchat-card">
654 + <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
655 + <div class="mxchat-autosave-section">
656 +<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__('for setup instructions and to see what\'s changed in the latest update.', 'mxchat'); ?></p> <table class="form-table">
657 + <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
658 + </table>
659 + </div>
463 660 </div>
661 + </div>
464 662
465 - <div class="section-divider"></div>
663 + <div id="general" class="mxchat-tab-content">
664 + <div class="mxchat-card">
665 + <?php do_settings_sections('mxchat-general'); ?>
666 + <div class="video-tutorials-section">
466 667
467 - <!-- Live Agent Settings Section -->
468 - <div class="mxchat-settings-section">
469 - <h2><?php echo esc_html__('Live Agent Settings', 'mxchat'); ?></h2>
470 - <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>
471 - <table class="form-table">
472 - <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
473 - </table>
474 - </div>
475 - </div>
476 - </div>
477 668
669 + <div class="support-notification">
670 + <div class="support-notification-icon">
671 + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
672 + <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
673 + <path d="M12 9v4"/>
674 + <path d="M12 17h.01"/>
675 + </svg>
676 + </div>
677 + <div class="support-notification-content">
678 + <h3 class="support-notification-title"><?php echo esc_html__('Need Help? We\'re Here for You!', 'mxchat'); ?></h3>
679 + <p class="support-notification-message">
680 + <?php echo esc_html__('Our goal is to provide the best experience and AI chatbot plugin available. If you\'re having trouble or believe something is not working as expected, please don\'t hesitate to submit a support ticket.', 'mxchat'); ?>
681 + </p>
682 + <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer" class="support-notification-button">
683 + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
684 + <path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/>
685 + <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/>
686 + </svg>
687 + <?php echo esc_html__('Submit Support Ticket', 'mxchat'); ?>
688 + </a>
689 + </div>
690 +</div>
478 691
479 - <div id="general" class="mxchat-tab-content">
480 - <?php do_settings_sections('mxchat-general'); ?>
481 - <p>
482 - <?php echo esc_html__('If you’re having trouble with setup or getting the responses you need, we encourage you to review our', 'mxchat'); ?>
483 - <a href="https://mxchat.ai/documentation/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('documentation', 'mxchat'); ?></a> <?php echo esc_html__('or', 'mxchat'); ?>
484 - <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer"><?php echo esc_html__('create a support ticket', 'mxchat'); ?></a>.
485 - </p>
486 - <p>
487 - <?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>.
488 - </p>
692 + <div class="tutorial-grid">
489 693
490 - <div class="faq-item">
491 - <h3><?php echo esc_html__('How does the Claude API integration work?', 'mxchat'); ?></h3>
492 - <p>
493 - <?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'); ?>
494 - </p>
495 - <p>
496 - <?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'); ?>
497 - </p>
498 - <p>
499 - <?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>.
500 - </p>
501 - </div>
694 + <div class="tutorial-item">
695 + <h3><?php echo esc_html__('AI Theme Generator Tutorial', 'mxchat'); ?></h3>
696 + <div class="video-description">
697 + <p><?php echo esc_html__('Learn how to instantly restyle your chatbot using plain English prompts. The AI Theme Generator lets you generate and apply beautiful designs with real-time previews—no CSS skills needed.', 'mxchat'); ?></p>
698 + <a href="https://www.youtube.com/watch?v=rSQDW2qbtRU&t" target="_blank" rel="noopener" class="video-link">
699 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
700 + <?php echo esc_html__('Watch AI Theme Generator Tutorial', 'mxchat'); ?>
701 + </a>
702 + </div>
703 +</div>
502 704
503 - <div class="faq-item">
504 - <h3><?php echo esc_html__('How does the X.AI API integration work?', 'mxchat'); ?></h3>
505 - <p>
506 - <?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'); ?>
507 - </p>
508 - <p>
509 - <?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>.
510 - </p>
705 +
706 + <div class="tutorial-item">
707 + <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
708 + <div class="video-description">
709 + <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
710 + <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
711 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
712 + <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
713 + </a>
511 714 </div>
715 + </div>
512 716
513 - <div class="faq-item">
514 - <h3><?php echo esc_html__('Do I need an OpenAI API key to use the chatbot?', 'mxchat'); ?></h3>
515 - <p>
516 - <?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'); ?>
517 - </p>
518 - <p>
519 - <?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'); ?>
520 - </p>
521 - </div>
717 +<div class="tutorial-item">
718 + <h3><?php echo esc_html__('Admin Assistant Add-on', 'mxchat'); ?></h3>
719 + <div class="video-description">
720 + <p><?php echo esc_html__('Discover how to use the MxChat Admin Assistant to bring a ChatGPT-like experience directly inside your WordPress dashboard. Learn to access multiple AI models, save conversations, generate images, and use web search - all without leaving your admin panel.', 'mxchat'); ?></p>
721 + <a href="https://youtu.be/AdEA1k-UCFM" target="_blank" rel="noopener" class="video-link">
722 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2-3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
723 + <?php echo esc_html__('Watch Admin Assistant Tutorial', 'mxchat'); ?>
724 + </a>
725 + </div>
726 +</div>
522 727
523 - <div class="faq-item">
524 - <h3><?php echo esc_html__('How do I add the chatbot to my site?', 'mxchat'); ?></h3>
525 - <p>
526 - <?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'); ?>
527 - </p>
728 + <div class="tutorial-item">
729 + <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
730 + <div class="video-description">
731 + <p><?php echo esc_html__('Discover how to use the Intent Tester to fine-tune your chatbot\'s responses and ensure it accurately understands user queries.', 'mxchat'); ?></p>
732 + <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
733 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
734 + <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
735 + </a>
528 736 </div>
737 + </div>
529 738
530 - <div class="faq-item">
531 - <h3><?php echo esc_html__('How does the chatbot use my content to generate responses?', 'mxchat'); ?></h3>
532 - <p>
533 - <?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'); ?>
534 - </p>
739 + <div class="tutorial-item">
740 + <h3><?php echo esc_html__('Theme Customizer Add-on', 'mxchat'); ?></h3>
741 + <div class="video-description">
742 + <p><?php echo esc_html__('Learn how to customize your chatbot appearance with the Theme Customizer add-on. Easily modify colors, fonts, and styles with real-time previews to match your brand perfectly.', 'mxchat'); ?></p>
743 + <a href="https://youtu.be/MfbB9mZi6ag" target="_blank" rel="noopener" class="video-link">
744 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
745 + <?php echo esc_html__('Watch Theme Customizer Tutorial', 'mxchat'); ?>
746 + </a>
535 747 </div>
748 + </div>
536 749
537 - <div class="faq-item">
538 - <h3><?php echo esc_html__('Why does the chatbot sometimes make up links or information?', 'mxchat'); ?></h3>
539 - <p>
540 - <?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'); ?>
541 - </p>
750 + <div class="tutorial-item">
751 + <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
752 + <div class="video-description">
753 + <p><?php echo esc_html__('See how to integrate MxChat with your WooCommerce store to provide product recommendations and shopping assistance to your customers.', 'mxchat'); ?></p>
754 + <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
755 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
756 + <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
757 + </a>
542 758 </div>
759 + </div>
543 760
544 - <div class="faq-item">
545 - <h3><?php echo esc_html__('How do intents work in MxChat?', 'mxchat'); ?></h3>
546 - <p>
547 - <?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>.
548 - </p>
761 + <div class="tutorial-item">
762 + <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
763 + <div class="video-description">
764 + <p><?php echo esc_html__('Learn how to set up your knowledge base using PDFs, sitemaps, and manual entries to enhance your chatbot\'s responses with site-specific information.', 'mxchat'); ?></p>
765 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
766 + <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
767 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
768 + <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
769 + </a>
549 770 </div>
771 + </div>
550 772
551 - <div class="faq-item">
552 - <h3><?php echo esc_html__('How does the Complianz integration work?', 'mxchat'); ?></h3>
553 - <p>
554 - <?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'); ?>
555 - </p>
773 + <div class="tutorial-item">
774 + <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
775 + <div class="video-description">
776 + <p><?php echo esc_html__('See how to use the MxChat toolbar to chat with PDF and Word documents for enhanced document analysis and information retrieval.', 'mxchat'); ?></p>
777 + <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
778 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
779 + <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
780 + </a>
556 781 </div>
782 + </div>
557 783
558 - <div class="faq-item">
559 - <h3><?php echo esc_html__('How does the WooCommerce integration work?', 'mxchat'); ?></h3>
560 - <p>
561 - <?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'); ?>
562 - </p>
784 + <div class="tutorial-item">
785 + <h3><?php echo esc_html__('MxChat Smart Recommender Tutorial', 'mxchat'); ?></h3>
786 + <div class="video-description">
787 + <p><?php echo esc_html__('Learn how to create intelligent recommendation flows that guide users to perfect matches based on their preferences.', 'mxchat'); ?></p>
788 + <a href="https://www.youtube.com/watch?v=8te1KPa238g&t=1s" target="_blank" rel="noopener" class="video-link">
789 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
790 + <?php echo esc_html__('Watch Smart Recommender Tutorial', 'mxchat'); ?>
791 + </a>
563 792 </div>
793 + </div>
564 794
565 - <div class="faq-item">
566 - <h3><?php echo esc_html__('Why isn\'t my chatbot responding as expected?', 'mxchat'); ?></h3>
567 - <p>
568 - <?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'); ?>
569 - </p>
570 - <p>
571 - <?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'); ?>
572 - </p>
795 + <div class="tutorial-item">
796 + <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
797 + <div class="video-description">
798 + <p><?php echo esc_html__('Learn how to integrate Perplexity with your chatbot for real-time web search capabilities. This tutorial covers intent recognition, the toolbar toggle button, and how to enable your chatbot to search the web and provide up-to-date information to your visitors.', 'mxchat'); ?></p>
799 + <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
800 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
801 + <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
802 + </a>
573 803 </div>
804 + </div>
574 805
575 - <div class="faq-item">
576 - <h3><?php echo esc_html__('What is Loops, and how do I get an API key?', 'mxchat'); ?></h3>
577 - <p>
578 - <?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'); ?>
579 - </p>
806 + <div class="tutorial-item">
807 + <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
808 + <div class="video-description">
809 + <p><?php echo esc_html__('Learn how to leverage Brave Search intent capabilities to improve your chatbot\'s understanding of user queries and provide more accurate responses.', 'mxchat'); ?></p>
810 + <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
811 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
812 + <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
813 + </a>
580 814 </div>
581 -
582 815 </div>
583 - </div>
584 - <?php
585 -}
586 816
587 -
588 -
589 -public function mxchat_create_transcripts_page() {
590 - global $wpdb;
591 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
592 -
593 - // Get basic stats
594 - $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
595 - $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
596 - $total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != ''");
597 - ?>
598 - <div class="wrap mxchat-admin">
599 - <!-- Stats Cards -->
600 - <div class="mxchat-stats-grid">
601 - <div class="mxchat-stat-card">
602 - <div class="stat-icon">💬</div>
603 - <div class="stat-content">
604 - <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
605 - <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
606 - </div>
817 + <div class="tutorial-item">
818 + <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
819 + <div class="video-description">
820 + <p><?php echo esc_html__('Discover how to set up email capture with MxChat using Loops to grow your mailing list while providing value through your chatbot.', 'mxchat'); ?></p>
821 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
822 + <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
823 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
824 + <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
825 + </a>
607 826 </div>
608 - <div class="mxchat-stat-card">
609 - <div class="stat-icon">📝</div>
610 - <div class="stat-content">
611 - <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
612 - <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
613 - </div>
614 - </div>
615 - <div class="mxchat-stat-card">
616 - <div class="stat-icon">👥</div>
617 - <div class="stat-content">
618 - <span class="stat-value"><?php echo esc_html($total_users); ?></span>
619 - <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
620 - </div>
621 - </div>
622 827 </div>
623 828
624 - <!-- Search and Filter Controls -->
625 - <div class="mxchat-controls-wrapper">
626 - <div class="mxchat-search-box">
627 - <input type="text" id="mxchat-search-transcripts"
628 - placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
629 - class="regular-text">
829 + <div class="tutorial-item">
830 + <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
831 + <div class="video-description">
832 + <p><?php echo esc_html__('Learn how to use the MxChat AI Agent Testing Service to evaluate and improve your chatbot\'s performance and accuracy.', 'mxchat'); ?></p>
833 + <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
834 + <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
835 + <span class="video-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19c-2.3 0-6.4-.2-8.1-.6-.7-.2-1.2-.7-1.4-1.4-.3-1.1-.5-3.4-.5-5s.2-3.9.5-5c.2-.7.7-1.2 1.4-1.4C5.6 5.2 9.7 5 12 5s6.4.2 8.1.6c.7.2 1.2.7 1.4 1.4.3 1.1.5 3.4.5 5s-.2 3.9-.5 5c-.2.7-.7 1.2-1.4 1.4-1.7.4-5.8.6-8.1.6z"></path><polygon points="10 15 15 12 10 9 10 15"></polygon></svg></span>
836 + <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
837 + </a>
630 838 </div>
839 + </div>
840 + </div>
631 841
632 - <form id="mxchat-delete-form" method="post">
633 - <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
634 - <div class="mxchat-controls">
635 - <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
636 - <span class="dashicons dashicons-yes-alt"></span>
637 - <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
638 - </button>
639 - <button type="submit" class="button delete-chats-button">
640 - <span class="dashicons dashicons-trash"></span>
641 - <?php esc_html_e('Delete Selected', 'mxchat'); ?>
642 - </button>
842 + </div>
643 843 </div>
644 - </form>
844 + </div>
645 845 </div>
646 -
647 - <!-- Transcripts Container -->
648 - <div id="mxchat-transcripts"></div>
649 846 </div>
650 847 <?php
651 848 }
849 +public function dismiss_live_agent_notice() {
850 + // Add debugging
851 + //error_log('dismiss_live_agent_notice called');
852 + //error_log('POST data: ' . print_r($_POST, true));
652 853
653 -public function mxchat_create_prompts_page() {
654 - global $wpdb;
655 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
854 + // Verify nonce
855 + if (!wp_verify_nonce($_POST['nonce'], 'dismiss_live_agent_notice')) {
856 + //error_log('Nonce verification failed');
857 + wp_die('Security check failed');
858 + }
656 859
657 - // Display success message if all prompts were deleted
658 - if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
659 - echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
860 + // Remove the notice flag
861 + $deleted = delete_option('mxchat_show_live_agent_disabled_notice');
862 + //error_log('Option deleted: ' . ($deleted ? 'yes' : 'no'));
863 +
864 + wp_send_json_success();
865 +}
866 +public function add_live_agent_nonce() {
867 + if (get_option('mxchat_show_live_agent_disabled_notice', false)) {
868 + // Make sure your admin script is enqueued and localize the data
869 + wp_localize_script('mxchat-admin-js', 'mxchatLiveAgent', array(
870 + 'nonce' => wp_create_nonce('dismiss_live_agent_notice'),
871 + 'ajaxurl' => admin_url('admin-ajax.php')
872 + ));
660 873 }
874 +}
661 875
662 - // Set up pagination and search query
663 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
664 - $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
665 - $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
666 - $per_page = 10;
667 - $offset = ($current_page - 1) * $per_page;
668 876
669 - // Modify query to handle search input
670 - $sql_search = "";
671 - if ($search_query) {
672 - $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
673 - }
877 +public function mxchat_create_transcripts_page() {
878 + global $wpdb;
879 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
674 880
675 - // Retrieve total number of prompts, considering search filter
676 - $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}");
677 - $total_pages = ceil($total_prompts / $per_page);
881 + // Get basic stats
882 + $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
883 + $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
678 884
679 - // Retrieve prompts from the database
680 - $prompts = $wpdb->get_results(
681 - $wpdb->prepare(
682 - "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
683 - $per_page,
684 - $offset
885 + // Count unique users with detailed breakdown
886 + $total_users = $wpdb->get_var("
887 + SELECT COUNT(DISTINCT
888 + CASE
889 + WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
890 + WHEN user_id != 0 THEN CONCAT('user_', user_id)
891 + WHEN user_identifier NOT LIKE 'Tech-Savvy User'
892 + AND user_identifier NOT LIKE 'Detail-Oriented User'
893 + AND user_identifier NOT LIKE 'Language Learner'
894 + AND user_identifier NOT LIKE 'Casual Browser'
895 + AND user_identifier NOT LIKE 'Policy Enforcer'
896 + AND user_identifier NOT LIKE 'Researcher'
897 + AND user_identifier NOT LIKE 'Loyalty Member'
898 + AND user_identifier NOT LIKE 'Gift Buyer'
899 + AND user_identifier NOT LIKE 'Parent or Caregiver'
900 + THEN user_identifier
901 + ELSE session_id
902 + END
685 903 )
686 - );
904 + FROM $table_name
905 + WHERE role != 'assistant'
906 + ");
687 907
688 - // Add the pagination links generation here
689 - $page_links = paginate_links(array(
690 - 'base' => add_query_arg(array(
691 - 'paged' => '%#%',
692 - 'search' => urlencode($search_query),
693 - '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
694 - ), admin_url('admin.php?page=mxchat-prompts')),
695 - 'format' => '',
696 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
697 - 'next_text' => __('Next &raquo;', 'mxchat'),
698 - 'total' => $total_pages,
699 - 'current' => $current_page,
700 - ));
908 + // Get user type breakdown
909 + $registered_users = $wpdb->get_var("
910 + SELECT COUNT(DISTINCT user_email)
911 + FROM $table_name
912 + WHERE user_email != '' AND user_email IS NOT NULL
913 + ");
701 914
702 - // Retrieve processing statuses
703 - $pdf_url = get_transient('mxchat_last_pdf_url');
704 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
705 - $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false;
706 - $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false;
915 + $guest_users = $wpdb->get_var("
916 + SELECT COUNT(DISTINCT user_identifier)
917 + FROM $table_name
918 + WHERE (user_email = '' OR user_email IS NULL)
919 + AND role != 'assistant'
920 + AND user_identifier NOT LIKE 'Tech-Savvy User'
921 + AND user_identifier NOT LIKE 'Detail-Oriented User'
922 + AND user_identifier NOT LIKE 'Language Learner'
923 + AND user_identifier NOT LIKE 'Casual Browser'
924 + AND user_identifier NOT LIKE 'Policy Enforcer'
925 + AND user_identifier NOT LIKE 'Researcher'
926 + AND user_identifier NOT LIKE 'Loyalty Member'
927 + AND user_identifier NOT LIKE 'Gift Buyer'
928 + AND user_identifier NOT LIKE 'Parent or Caregiver'
929 + ");
707 930
708 - if ($pdf_status && $pdf_status['status'] === 'complete') {
709 - delete_transient('mxchat_last_pdf_url');
710 - $pdf_status = false;
711 - }
712 - if ($sitemap_status && $sitemap_status['status'] === 'complete') {
713 - delete_transient('mxchat_last_sitemap_url');
714 - $sitemap_status = false;
715 - }
716 -
717 - $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
718 - || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
931 + // Get agent test messages count
932 + $agent_tests = $wpdb->get_var("
933 + SELECT COUNT(DISTINCT session_id)
934 + FROM $table_name
935 + WHERE user_identifier IN (
936 + 'Tech-Savvy User',
937 + 'Detail-Oriented User',
938 + 'Language Learner',
939 + 'Casual Browser',
940 + 'Policy Enforcer',
941 + 'Researcher',
942 + 'Loyalty Member',
943 + 'Gift Buyer',
944 + 'Parent or Caregiver'
945 + )
946 + ");
719 947 ?>
720 - <div class="wrap mxchat-wrapper">
948 + <div class="wrap mxchat-transcripts-wrapper">
721 949 <!-- Hero Section -->
722 - <div class="mxchat-hero">
950 + <div class="mxchat-transcripts-hero">
723 951 <h1 class="mxchat-main-title">
724 - <span class="mxchat-gradient-text">Knowledge Base</span> Manager
952 + Chat <span class="mxchat-gradient-text">Transcripts</span>
725 953 </h1>
726 954 <p class="mxchat-hero-subtitle">
727 - <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
955 + <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
728 956 </p>
729 957 </div>
730 -
731 958 <div class="mxchat-content">
732 - <!-- Import Settings Card -->
733 - <div class="mxchat-card">
734 - <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
735 - <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>
736 -
737 - <!-- Tab Contents -->
738 - <div class="mxchat-tab-contents">
739 - <!-- Default Database Tab -->
740 - <div id="default-db" class="mxchat-tab-content active">
741 - <!-- Auto-Sync Settings -->
742 - <div class="mxchat-settings-section">
743 - <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
744 - <p class="mxchat-description">
745 - <?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'); ?>
746 - </p>
747 - <div class="mxchat-autosave-section"> <!-- Add this wrapper -->
748 - <div class="mxchat-toggle-group">
749 - <div class="mxchat-toggle-container">
750 - <label class="mxchat-toggle-switch">
751 - <input type="checkbox"
752 - name="mxchat_auto_sync_posts"
753 - class="mxchat-autosave-field"
754 - value="1"
755 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
756 - <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
757 - <span class="mxchat-toggle-slider"></span>
758 - </label>
759 - <span class="mxchat-toggle-label">
760 - <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
761 - </span>
762 - </div>
763 - <div class="mxchat-toggle-container">
764 - <label class="mxchat-toggle-switch">
765 - <input type="checkbox"
766 - name="mxchat_auto_sync_pages"
767 - class="mxchat-autosave-field"
768 - value="1"
769 - data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
770 - <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
771 - <span class="mxchat-toggle-slider"></span>
772 - </label>
773 - <span class="mxchat-toggle-label">
774 - <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
775 - </span>
776 - </div>
777 - </div>
778 - </div>
959 + <!-- Stats Cards -->
960 + <div class="mxchat-stats-grid">
961 + <div class="mxchat-stat-card">
962 + <div class="stat-icon">💬</div>
963 + <div class="stat-content">
964 + <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
965 + <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
779 966 </div>
780 -
781 - <!-- Import Methods -->
782 - <div class="mxchat-import-methods">
783 - <div class="mxchat-method-card">
784 - <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
785 - <p><?php esc_html_e('Import all content from your sitemap automatically.', 'mxchat'); ?></p>
786 - </div>
787 - <div class="mxchat-method-card">
788 - <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
789 - <p><?php esc_html_e('Import knowledge directly from PDF documents.', 'mxchat'); ?></p>
790 - </div>
791 - <div class="mxchat-method-card">
792 - <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
793 - <p><?php esc_html_e('Import content from any specific webpage.', 'mxchat'); ?></p>
794 - </div>
795 - <div class="mxchat-method-card">
796 - <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
797 - <p><?php esc_html_e('Directly submit content to be vectorized.', 'mxchat'); ?></p>
798 - </div>
799 - </div>
800 -
801 - <!-- Import Form -->
802 - <?php if (!$is_processing) : ?>
803 - <div class="mxchat-import-form">
804 - <form id="mxchat-sitemap-form" method="post"
805 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
806 - <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
807 - <div class="mxchat-url-input-group">
808 - <input type="url"
809 - name="sitemap_url"
810 - id="sitemap_url"
811 - placeholder="<?php esc_attr_e('Enter Sitemap, PDF, or Webpage URL', 'mxchat'); ?>"
812 - required />
813 - <button type="submit"
814 - name="submit_sitemap"
815 - class="mxchat-button-primary">
816 - <?php esc_html_e('Import', 'mxchat'); ?>
817 - </button>
818 - </div>
819 - </form>
820 - </div>
821 - <?php endif; ?>
822 -
823 - <!-- Processing Status -->
824 - <?php if ($pdf_status && $pdf_status['status'] !== 'complete') : ?>
825 - <div class="mxchat-status-card">
826 - <div class="mxchat-status-header">
827 - <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
828 - <?php if ($is_processing) : ?>
829 - <form method="post" class="mxchat-stop-form"
830 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
831 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
832 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
833 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
834 - </button>
835 - </form>
836 - <?php endif; ?>
837 - </div>
838 - <div class="mxchat-progress-bar">
839 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
840 - </div>
841 - <div class="mxchat-status-details">
842 - <p><?php printf(
843 - esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
844 - absint($pdf_status['processed_pages']),
845 - absint($pdf_status['total_pages']),
846 - absint($pdf_status['percentage'])
847 - ); ?></p>
848 - <p><?php printf(
849 - esc_html__('Status: %s', 'mxchat'),
850 - esc_html(ucfirst($pdf_status['status']))
851 - ); ?></p>
852 - <p><?php printf(
853 - esc_html__('Last update: %s', 'mxchat'),
854 - esc_html($pdf_status['last_update'])
855 - ); ?></p>
856 - </div>
857 - </div>
858 - <?php endif; ?>
859 -
860 - <?php if ($sitemap_status && $sitemap_status['status'] !== 'complete') : ?>
861 - <div class="mxchat-status-card">
862 - <div class="mxchat-status-header">
863 - <h4><?php esc_html_e('Sitemap Processing Status (Refresh for update)', 'mxchat'); ?></h4>
864 - <?php if ($is_processing) : ?>
865 - <form method="post" class="mxchat-stop-form"
866 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
867 - <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
868 - <button type="submit" name="stop_processing" class="mxchat-button-secondary">
869 - <?php esc_html_e('Stop Processing', 'mxchat'); ?>
870 - </button>
871 - </form>
872 - <?php endif; ?>
873 - </div>
874 - <div class="mxchat-progress-bar">
875 - <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
876 - </div>
877 - <div class="mxchat-status-details">
878 - <p><?php printf(
879 - esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
880 - absint($sitemap_status['processed_urls']),
881 - absint($sitemap_status['total_urls']),
882 - absint($sitemap_status['percentage'])
883 - ); ?></p>
884 - <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
885 - <div class="mxchat-error-notice">
886 - <?php if (!empty($sitemap_status['error'])) : ?>
887 - <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
888 - <?php endif; ?>
889 - <?php if (!empty($sitemap_status['last_error'])) : ?>
890 - <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
891 - <?php endif; ?>
892 - </div>
893 - <?php endif; ?>
894 - </div>
895 - </div>
896 - <?php endif; ?>
967 + </div>
968 + <div class="mxchat-stat-card">
969 + <div class="stat-icon">📝</div>
970 + <div class="stat-content">
971 + <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
972 + <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
897 973 </div>
974 + </div>
975 + <div class="mxchat-stat-card">
976 + <div class="stat-icon">👥</div>
977 + <div class="stat-content">
978 + <span class="stat-value"><?php echo esc_html($total_users); ?></span>
979 + <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
980 + <span class="stat-sublabel">
981 + <?php
982 + echo sprintf(
983 + esc_html__('%d registered, %d guests, %d agent tests', 'mxchat'),
984 + $registered_users,
985 + $guest_users,
986 + $agent_tests
987 + );
988 + ?>
989 + </span>
990 + </div>
991 + </div>
992 + </div>
898 993
899 -
994 + <!-- Search and Filter Controls with Notification Settings Button -->
995 + <div class="mxchat-controls-wrapper">
996 + <div class="mxchat-search-box">
997 + <input type="text" id="mxchat-search-transcripts"
998 + placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
999 + class="regular-text">
900 1000 </div>
1001 + <form id="mxchat-delete-form" method="post">
1002 + <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
1003 + <div class="mxchat-controls">
1004 + <button type="button" id="mxchat-chat-email-notification-btn" class="mxchat-action-button">
1005 + <span class="dashicons dashicons-email"></span>
1006 + <?php esc_html_e('Notification Settings', 'mxchat'); ?>
1007 + </button>
1008 + <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
1009 + <span class="dashicons dashicons-download"></span>
1010 + <?php esc_html_e('Export All Chats', 'mxchat'); ?>
1011 + </button>
1012 + <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
1013 + <span class="dashicons dashicons-yes-alt"></span>
1014 + <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
1015 + </button>
1016 + <button type="submit" class="button delete-chats-button">
1017 + <span class="dashicons dashicons-trash"></span>
1018 + <?php esc_html_e('Delete Selected', 'mxchat'); ?>
1019 + </button>
1020 + </div>
1021 + </form>
901 1022 </div>
902 1023
903 - <!-- Direct Content Submission Card -->
904 -<div class="mxchat-card">
905 - <div class="mxchat-card-header">
906 - <h2><?php esc_html_e('Submit Direct Content', 'mxchat'); ?></h2>
1024 + <!-- Transcripts Container -->
1025 + <div id="mxchat-transcripts"></div>
1026 + </div>
907 1027 </div>
908 - <form id="mxchat-content-form" method="post"
909 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
910 - <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
911 - <div class="mxchat-form-group">
912 - <label for="article_content"><?php esc_html_e('Content:', 'mxchat'); ?></label>
913 - <textarea name="article_content" id="article_content" required
914 - placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"></textarea>
915 - </div>
916 - <div class="mxchat-form-group">
917 - <label for="article_url"><?php esc_html_e('Source URL (Optional):', 'mxchat'); ?></label>
918 - <input type="url" name="article_url" id="article_url"
919 - placeholder="<?php esc_attr_e('Enter source URL', 'mxchat'); ?>">
920 - </div>
921 - <button type="submit" name="submit_content" class="mxchat-button-primary">
922 - <?php esc_html_e('Submit Content', 'mxchat'); ?>
923 - </button>
924 - </form>
925 -</div>
926 1028
927 -<!-- Knowledge Base Table Card -->
928 -<div class="mxchat-card">
929 - <div class="mxchat-card-header">
930 - <h2><?php esc_html_e('Knowledge Base', 'mxchat'); ?></h2>
931 - <div class="mxchat-header-actions">
932 - <!-- Search -->
933 - <form method="get" id="knowledge-search" class="mxchat-search-form">
934 - <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
935 - <input type="hidden" name="page" value="mxchat-prompts" />
936 - <div class="mxchat-search-group">
937 - <span class="dashicons dashicons-search"></span>
938 - <input type="text"
939 - name="search"
940 - placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
941 - value="<?php echo esc_attr($search_query); ?>" />
942 - </div>
943 - </form>
944 -
945 - <!-- Delete All -->
946 - <form method="post"
947 - action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
948 - class="mxchat-delete-form"
949 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
950 - <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
951 - <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
952 - <span class="dashicons dashicons-trash"></span>
953 - <?php esc_html_e('Delete All', 'mxchat'); ?>
954 - </button>
955 - </form>
1029 + <!-- Modal for Chat Email Notification Settings -->
1030 + <div id="mxchat-chat-email-notification-modal" class="mxchat-chat-notification-modal-overlay" style="display: none;">
1031 + <div class="mxchat-chat-notification-modal-content">
1032 + <div class="mxchat-chat-notification-modal-header">
1033 + <h2><?php esc_html_e('Email Notification Settings', 'mxchat'); ?></h2>
1034 + <button type="button" class="mxchat-chat-notification-modal-close">&times;</button>
1035 + </div>
1036 + <div class="mxchat-chat-notification-modal-body">
1037 + <form method="post" action="options.php" id="mxchat-chat-email-notification-form">
1038 + <?php
1039 + settings_fields('mxchat_transcripts_options');
1040 + do_settings_sections('mxchat-transcripts');
1041 + ?>
1042 + <div class="mxchat-chat-notification-modal-footer">
1043 + <button type="submit" class="button button-primary">
1044 + <?php esc_html_e('Save Notification Settings', 'mxchat'); ?>
1045 + </button>
1046 + <button type="button" class="button mxchat-chat-notification-modal-cancel">
1047 + <?php esc_html_e('Cancel', 'mxchat'); ?>
1048 + </button>
1049 + </div>
1050 + </form>
1051 + </div>
956 1052 </div>
957 1053 </div>
1054 + <?php
1055 +}
958 1056
959 - <!-- Table -->
960 - <div class="mxchat-table-wrapper">
961 - <table class="mxchat-records-table">
962 - <thead>
963 - <tr>
964 - <th><?php esc_html_e('ID', 'mxchat'); ?></th>
965 - <th><?php esc_html_e('Content', 'mxchat'); ?></th>
966 - <th><?php esc_html_e('Source', 'mxchat'); ?></th>
967 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
968 - </tr>
969 - </thead>
970 - <tbody>
971 - <?php if ($prompts) : ?>
972 - <?php foreach ($prompts as $prompt) : ?>
973 - <tr id="prompt-<?php echo esc_attr($prompt->id); ?>">
974 - <td><?php echo esc_html($prompt->id); ?></td>
975 - <td class="mxchat-content-cell">
976 - <div class="content-view">
977 - <?php echo wp_kses_post(wpautop(esc_textarea($prompt->article_content))); ?>
978 - </div>
979 - <textarea class="content-edit" style="display:none;">
980 - <?php echo esc_textarea($prompt->article_content); ?>
981 - </textarea>
982 - </td>
983 - <td class="mxchat-url-cell">
984 - <?php if (!empty($prompt->source_url)) : ?>
985 - <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
986 - <span class="dashicons dashicons-external"></span>
987 - <?php esc_html_e('View Source', 'mxchat'); ?>
988 - </a>
989 - <?php else : ?>
990 - <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
991 - <?php endif; ?>
992 - </td>
993 - <td class="mxchat-actions-cell">
994 - <button class="mxchat-button-icon edit-button"
995 - data-id="<?php echo esc_attr($prompt->id); ?>">
996 - <span class="dashicons dashicons-edit"></span>
997 - </button>
998 - <button class="mxchat-button-icon save-button"
999 - data-id="<?php echo esc_attr($prompt->id); ?>"
1000 - style="display:none;"
1001 - data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
1002 - <span class="dashicons dashicons-saved"></span>
1003 - </button>
1004 - <a href="<?php echo esc_url(admin_url(
1005 - 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1006 - . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1007 - )); ?>"
1008 - class="mxchat-button-icon delete-button"
1009 - onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1010 - <span class="dashicons dashicons-trash"></span>
1011 - </a>
1012 - </td>
1013 - </tr>
1014 - <?php endforeach; ?>
1015 - <?php else : ?>
1016 - <tr>
1017 - <td colspan="4" class="mxchat-no-records">
1018 - <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1019 - </td>
1020 - </tr>
1021 - <?php endif; ?>
1022 - </tbody>
1023 - </table>
1024 - </div>
1025 1057
1026 - <?php if ($page_links) : ?>
1027 - <div class="mxchat-pagination">
1028 - <?php echo wp_kses_post($page_links); ?>
1029 - </div>
1030 - <?php endif; ?>
1031 -</div>
1032 -</div>
1033 -</div>
1034 -<?php
1058 +public function mxchat_transcripts_notification_section_callback() {
1059 + echo '<p>' . esc_html__('Configure email notifications for new chat transcripts. You will receive an email notification when a new chat session begins.', 'mxchat') . '</p>';
1035 1060 }
1061 +public function mxchat_enable_notifications_callback() {
1062 + $options = get_option('mxchat_transcripts_options', array());
1063 + $enabled = isset($options['mxchat_enable_notifications']) ? $options['mxchat_enable_notifications'] : 0;
1064 + ?>
1065 + <label for="mxchat_enable_notifications">
1066 + <input type="checkbox" id="mxchat_enable_notifications"
1067 + name="mxchat_transcripts_options[mxchat_enable_notifications]"
1068 + value="1" <?php checked(1, $enabled); ?>>
1069 + <?php esc_html_e('Send email notification when a new chat session starts', 'mxchat'); ?>
1070 + </label>
1071 + <p class="description">
1072 + <?php esc_html_e('Enable this option to receive email notifications for new chat sessions.', 'mxchat'); ?>
1073 + </p>
1074 + <?php
1075 +}
1076 +public function mxchat_notification_email_callback() {
1077 + $options = get_option('mxchat_transcripts_options', array());
1078 + $email = isset($options['mxchat_notification_email']) ? $options['mxchat_notification_email'] : get_option('admin_email');
1079 + ?>
1080 + <input type="email" id="mxchat_notification_email"
1081 + name="mxchat_transcripts_options[mxchat_notification_email]"
1082 + value="<?php echo esc_attr($email); ?>"
1083 + class="regular-text">
1084 + <p class="description">
1085 + <?php esc_html_e('Enter the email address where notifications should be sent. Defaults to the admin email address.', 'mxchat'); ?>
1086 + </p>
1087 + <?php
1088 +}
1089 +public function sanitize_transcripts_options($input) {
1090 + $sanitized = array();
1036 1091
1092 + $sanitized['mxchat_enable_notifications'] = isset($input['mxchat_enable_notifications']) ? 1 : 0;
1037 1093
1038 -public function mxchat_handle_delete_all_prompts() {
1039 - // Verify nonce
1040 - if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
1041 - wp_die(__('Nonce verification failed.', 'mxchat'));
1094 + if (isset($input['mxchat_notification_email'])) {
1095 + $sanitized['mxchat_notification_email'] = sanitize_email($input['mxchat_notification_email']);
1096 + if (!is_email($sanitized['mxchat_notification_email'])) {
1097 + add_settings_error(
1098 + 'mxchat_transcripts_options',
1099 + 'invalid_email',
1100 + __('Please enter a valid email address for notifications.', 'mxchat'),
1101 + 'error'
1102 + );
1103 + $sanitized['mxchat_notification_email'] = get_option('admin_email');
1104 + }
1042 1105 }
1043 1106
1044 - // Check permissions
1107 + return $sanitized;
1108 +}
1109 +public function export_chat_transcripts() {
1045 1110 if (!current_user_can('manage_options')) {
1046 - wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
1111 + wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
1047 1112 }
1048 1113
1114 + check_ajax_referer('mxchat_export_transcripts', 'security');
1115 +
1049 1116 global $wpdb;
1050 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1117 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1051 1118
1052 - // Delete all prompts from the table
1053 - $wpdb->query("DELETE FROM {$table_name}");
1119 + // Get all transcripts ordered by session and timestamp
1120 + $results = $wpdb->get_results(
1121 + "SELECT session_id, user_email, user_identifier, role, message, timestamp
1122 + FROM {$table_name}
1123 + ORDER BY session_id, timestamp ASC"
1124 + );
1054 1125
1055 - // Clear relevant cache
1056 - wp_cache_delete('all_prompts', 'mxchat_prompts');
1126 + if (empty($results)) {
1127 + wp_send_json_error(array('message' => 'No transcripts found.'));
1128 + wp_die();
1129 + }
1057 1130
1058 - // Redirect back with a success message
1059 - $redirect_url = add_query_arg(array(
1060 - 'page' => 'mxchat-prompts',
1061 - 'all_deleted' => 'true'
1062 - ), admin_url('admin.php'));
1131 + // Set headers for CSV download
1132 + header('Content-Type: text/csv');
1133 + header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
1134 + header('Pragma: no-cache');
1135 + header('Expires: 0');
1063 1136
1064 - wp_safe_redirect($redirect_url);
1065 - exit;
1066 -}
1067 -public function mxchat_handle_delete_prompt() {
1068 - // Sanitize and validate nonce
1069 - $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
1070 - if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
1071 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1072 - }
1137 + // Create output stream
1138 + $output = fopen('php://output', 'w');
1073 1139
1074 - // Check permissions
1075 - if (!current_user_can('manage_options')) {
1076 - wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
1077 - }
1140 + // Add UTF-8 BOM for proper Excel encoding
1141 + fputs($output, "\xEF\xBB\xBF");
1078 1142
1079 - // Validate and sanitize ID parameter
1080 - $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
1081 - if ($id <= 0) {
1082 - wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
1143 + // Add CSV headers
1144 + fputcsv($output, array(
1145 + 'Session ID',
1146 + 'Email',
1147 + 'User Identifier',
1148 + 'Role',
1149 + 'Message',
1150 + 'Timestamp'
1151 + ));
1152 +
1153 + // Add data rows
1154 + foreach ($results as $row) {
1155 + fputcsv($output, array(
1156 + $row->session_id,
1157 + $row->user_email,
1158 + $row->user_identifier,
1159 + $row->role,
1160 + $row->message,
1161 + $row->timestamp
1162 + ));
1083 1163 }
1084 1164
1085 - global $wpdb;
1086 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1087 -
1088 - // Clear cache and delete prompt
1089 - wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
1090 - $wpdb->delete($table_name, array('id' => $id), array('%d'));
1091 -
1092 - wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php')));
1093 - exit;
1165 + fclose($output);
1166 + wp_die();
1094 1167 }
1095 -public function mxchat_generate_embedding($text) {
1096 - $options = get_option('mxchat_options');
1097 - $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat');
1098 1168
1099 - $response = wp_remote_post('https://api.openai.com/v1/embeddings', array(
1100 - 'body' => wp_json_encode(array(
1101 - 'model' => esc_html__('text-embedding-ada-002', 'mxchat'),
1102 - 'input' => $text
1103 - )),
1104 - 'headers' => array(
1105 - 'Authorization' => 'Bearer ' . $api_key,
1106 - 'Content-Type' => esc_html__('application/json', 'mxchat')
1107 - ),
1108 - ));
1109 1169
1110 - if (is_wp_error($response)) {
1111 - return null;
1112 - }
1170 +public function mxchat_fetch_chat_history() {
1171 + global $wpdb;
1172 + $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1173 + $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1113 1174
1114 - $response_data = json_decode(wp_remote_retrieve_body($response), true);
1115 - return $response_data['data'][0]['embedding'] ?? null;
1175 + if (!current_user_can('manage_options')) {
1176 + wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1116 1177 }
1117 -public function mxchat_delete_chat_history() {
1118 - if (!current_user_can('manage_options')) {
1119 - echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
1120 - wp_die();
1121 - }
1122 1178
1123 - check_ajax_referer('mxchat_delete_chat_history', 'security');
1179 + $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1180 + $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
1181 + $offset = ($page - 1) * $per_page;
1182 + $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
1124 1183
1125 - global $wpdb;
1126 - $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1184 + $search_condition = '';
1185 + $search_params = [];
1127 1186
1128 - if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
1129 - foreach ($_POST['delete_session_ids'] as $session_id) {
1130 - $session_id_sanitized = sanitize_text_field($session_id);
1187 + if (!empty($search)) {
1188 + $search_condition = "WHERE (
1189 + session_id LIKE %s
1190 + OR user_email LIKE %s
1191 + OR user_name LIKE %s
1192 + OR user_identifier LIKE %s
1193 + OR message LIKE %s
1194 + )";
1195 + $search_params = array_fill(0, 5, '%' . $wpdb->esc_like($search) . '%');
1196 + }
1131 1197
1132 - // Clear relevant cache before deletion
1133 - $cache_key = 'chat_session_' . $session_id_sanitized;
1134 - wp_cache_delete($cache_key, 'mxchat_chat_sessions');
1198 + $count_query = !empty($search)
1199 + ? $wpdb->prepare("SELECT COUNT(DISTINCT session_id) FROM {$table_name} {$search_condition}", $search_params)
1200 + : "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
1135 1201
1136 - // Perform the deletion
1137 - $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
1138 - }
1202 + $total_sessions = $wpdb->get_var($count_query);
1139 1203
1140 - // Optionally, clear a general cache if you have one
1141 - wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
1204 + $session_query = !empty($search)
1205 + ? $wpdb->prepare(
1206 + "SELECT DISTINCT t.session_id FROM {$table_name} t {$search_condition}
1207 + GROUP BY t.session_id ORDER BY MAX(t.timestamp) DESC LIMIT %d OFFSET %d",
1208 + array_merge($search_params, [$per_page, $offset])
1209 + )
1210 + : $wpdb->prepare(
1211 + "SELECT DISTINCT session_id FROM {$table_name}
1212 + GROUP BY session_id ORDER BY MAX(timestamp) DESC LIMIT %d OFFSET %d",
1213 + $per_page, $offset
1214 + );
1142 1215
1143 - echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]);
1144 - } else {
1145 - echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
1216 + $session_ids = $wpdb->get_col($session_query);
1217 +
1218 + if (empty($session_ids)) {
1219 + ob_start();
1220 + echo '<div class="mxchat-no-results">';
1221 + echo esc_html__('No chat history found.', 'mxchat');
1222 + if (!empty($search)) {
1223 + echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
1146 1224 }
1147 -
1225 + echo '</div>';
1226 + wp_send_json([
1227 + 'html' => ob_get_clean(),
1228 + 'page' => $page,
1229 + 'total_pages' => 0,
1230 + 'total_sessions' => 0
1231 + ]);
1148 1232 wp_die();
1149 1233 }
1150 1234
1235 + $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
1236 + $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
1151 1237
1152 -public function mxchat_save_inline_prompt() {
1153 - // Check for nonce security
1154 - check_ajax_referer('mxchat_save_inline_nonce');
1238 + ob_start();
1239 + echo '<div class="mxchat-transcript">';
1155 1240
1156 - // Verify permissions
1157 - if (!current_user_can('manage_options')) {
1158 - wp_send_json_error(esc_html__('Permission denied.', 'mxchat'));
1159 - return;
1160 - }
1241 + foreach ($session_ids as $session_id) {
1242 + $session_data = $originating_columns_exist
1243 + ? $wpdb->get_row($wpdb->prepare(
1244 + "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
1245 + FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
1246 + $session_id
1247 + ))
1248 + : $wpdb->get_row($wpdb->prepare(
1249 + "SELECT user_email, user_name, user_identifier, timestamp FROM {$table_name}
1250 + WHERE session_id = %s LIMIT 1",
1251 + $session_id
1252 + ));
1161 1253
1162 - global $wpdb;
1163 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1254 + $clicked_urls = [];
1255 + if ($url_table_exists) {
1256 + $url_clicks = $wpdb->get_results(
1257 + $wpdb->prepare(
1258 + "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
1259 + WHERE session_id = %s ORDER BY click_timestamp ASC",
1260 + $session_id
1261 + )
1262 + );
1263 + foreach ($url_clicks as $click) {
1264 + $clicked_urls[] = $click->clicked_url;
1265 + }
1266 + }
1164 1267
1165 - // Validate and sanitize input data
1166 - $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
1167 - $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : '';
1168 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1268 + $messages = $wpdb->get_results(
1269 + $wpdb->prepare(
1270 + "SELECT * FROM {$table_name}
1271 + WHERE session_id = %s ORDER BY timestamp ASC",
1272 + $session_id
1273 + )
1274 + );
1169 1275
1170 - if ($prompt_id > 0 && !empty($article_content)) {
1171 - // Re-generate the embedding vector for the updated content
1172 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1276 + $latest_timestamp = !empty($messages) ? end($messages)->timestamp : $session_data->timestamp;
1277 + $session_time = wp_date('M j, g:ia', strtotime($latest_timestamp . ' UTC'));
1173 1278
1174 - if (is_array($embedding_vector)) {
1175 - // Serialize the embedding vector before storing it
1176 - $embedding_vector_serialized = serialize($embedding_vector);
1279 + $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
1280 + $user_name = !empty($session_data->user_name) ? $session_data->user_name : ''; // NEW
1281 + $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
1177 1282
1178 - // Update the prompt in the database
1179 - $updated = $wpdb->update(
1180 - $table_name,
1181 - array(
1182 - 'article_content' => $article_content,
1183 - 'embedding_vector' => $embedding_vector_serialized,
1184 - 'source_url' => $article_url,
1185 - ),
1186 - array('id' => $prompt_id),
1187 - array('%s', '%s', '%s'),
1188 - array('%d')
1189 - );
1190 -
1191 - if ($updated !== false) {
1192 - wp_send_json_success();
1193 - } else {
1194 - wp_send_json_error(esc_html__('Database update failed.', 'mxchat'));
1283 + // NEW: Build user display with name if available
1284 + if (!empty($user_email)) {
1285 + $user_display = $user_email;
1286 + if (!empty($user_name)) {
1287 + $user_display .= ' (' . $user_name . ')';
1195 1288 }
1196 1289 } else {
1197 - wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat'));
1290 + $user_display = $user_identifier;
1198 1291 }
1199 - } else {
1200 - wp_send_json_error(esc_html__('Invalid data.', 'mxchat'));
1201 - }
1202 -}
1292 + echo '<div class="mxchat-session">';
1293 + echo '<div class="mxchat-session-header" data-session-id="' . esc_attr($session_id) . '">';
1294 + echo '<div class="mxchat-user-row">' . esc_html($user_display) . '</div>';
1203 1295
1296 + if ($originating_columns_exist && !empty($session_data->originating_page_url)) {
1297 + $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : $session_data->originating_page_url;
1204 1298
1299 + echo '<div class="mxchat-header-row flex-between">';
1300 + echo '<div class="mxchat-main-info">';
1301 + echo '<a href="' . esc_url($session_data->originating_page_url) . '" target="_blank" class="mxchat-page-link">';
1302 + echo esc_html($page_title);
1303 + echo '</a>';
1304 + echo '<span class="mxchat-session-time">' . esc_html($session_time) . '</span>';
1305 + echo '</div>';
1306 + echo '</div>';
1307 + }
1205 1308
1206 -// Add this method to handle post updates
1207 -public function handle_post_update($post_id, $post, $update) {
1208 - // Basic validation checks
1209 - if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) {
1210 - return;
1211 - }
1309 + if (!empty($clicked_urls)) {
1310 + echo '<div class="mxchat-links-row">';
1311 + echo '<span class="mxchat-label">Clicked:</span> ';
1312 + $link_count = 0;
1313 + foreach ($clicked_urls as $url) {
1314 + if ($link_count > 0) echo ', ';
1315 + echo '<a href="' . esc_url($url) . '" target="_blank" class="mxchat-link-item">' . esc_html(parse_url($url, PHP_URL_HOST)) . '</a>';
1316 + $link_count++;
1317 + if ($link_count >= 3) {
1318 + if (count($clicked_urls) > 3) {
1319 + echo ' +' . (count($clicked_urls) - 3) . ' more';
1320 + }
1321 + break;
1322 + }
1323 + }
1324 + echo '</div>';
1325 + }
1212 1326
1213 - // Only process published content
1214 - if (!in_array($post->post_status, array('publish'))) {
1215 - return;
1216 - }
1327 + echo '</div>'; // end session-header
1328 + echo '<div class="mxchat-messages">';
1217 1329
1218 - // Check if sync is enabled for this post type
1219 - $post_type = $post->post_type;
1220 - if (!in_array($post_type, array('post', 'page'))) {
1221 - return;
1222 - }
1330 + foreach ($messages as $transcript) {
1331 + $formatted_timestamp = wp_date('F j, Y g:i a', strtotime($transcript->timestamp . ' UTC'));
1223 1332
1224 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1225 - if (get_option($sync_option) != '1') {
1226 - return;
1227 - }
1333 + switch ($transcript->role) {
1334 + case 'assistant':
1335 + case 'bot':
1336 + $message_class = 'bot-message';
1337 + $display_role = esc_html__('Chatbot', 'mxchat');
1338 + break;
1339 + case 'user':
1340 + $message_class = 'user-message';
1341 + $display_role = !empty($transcript->user_identifier)
1342 + ? sanitize_text_field($transcript->user_identifier)
1343 + : esc_html__('User', 'mxchat');
1344 + break;
1345 + case 'agent':
1346 + $message_class = 'agent-message';
1347 + $display_role = esc_html__('Agent', 'mxchat');
1348 + break;
1349 + default:
1350 + $message_class = 'unknown-message';
1351 + $display_role = esc_html__('Unknown', 'mxchat');
1352 + }
1228 1353
1229 - // Prepare content and URL
1230 - $content = wp_strip_all_tags($post->post_content);
1231 - $url = get_permalink($post_id);
1354 + $message_content = wp_kses(
1355 + stripslashes($transcript->message),
1356 + [
1357 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1358 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1359 + 'a' => ['href' => [], 'title' => [], 'target' => []]
1360 + ]
1361 + );
1362 + $message_content = nl2br($message_content);
1232 1363
1233 - // Generate embedding vector
1234 - $embedding_vector = $this->mxchat_generate_embedding($content);
1235 - if (!$embedding_vector) {
1236 - return;
1364 + echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1365 + echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1366 + echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1367 + echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1368 + echo '</div>';
1369 + }
1370 +
1371 + echo '</div></div>'; // end messages and session
1237 1372 }
1238 1373
1239 - // Check for Pinecone addon and its settings
1240 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1241 - $use_pinecone = false;
1374 + echo '</div>'; // end transcript
1242 1375
1243 - if ($pinecone_settings && is_array($pinecone_settings)) {
1244 - // Check if Pinecone is enabled and all required settings are present
1245 - $use_pinecone = (
1246 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1247 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1248 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1249 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1250 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1251 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1252 - );
1253 - }
1376 + $total_pages = ceil($total_sessions / $per_page);
1254 1377
1255 - if ($use_pinecone) {
1256 - // Use Pinecone
1257 - $pinecone_result = $this->store_in_pinecone_main(
1258 - $embedding_vector,
1259 - $content,
1260 - $url,
1261 - $pinecone_settings['mxchat_pinecone_api_key'],
1262 - $pinecone_settings['mxchat_pinecone_environment'],
1263 - $pinecone_settings['mxchat_pinecone_index']
1264 - );
1378 + echo '<div class="mxchat-pagination">';
1379 + echo '<div class="mxchat-pagination-info">';
1380 + echo sprintf(
1381 + esc_html__('Showing %1$d to %2$d of %3$d sessions', 'mxchat'),
1382 + $offset + 1,
1383 + min($offset + $per_page, $total_sessions),
1384 + $total_sessions
1385 + );
1386 + echo '</div>';
1265 1387
1266 - if (!$pinecone_result['success']) {
1267 - //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB');
1268 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1388 + if ($total_pages > 1) {
1389 + echo '<div class="mxchat-pagination-controls">';
1390 + if ($page > 1) {
1391 + echo '<button class="mxchat-pagination-button" data-page="' . ($page - 1) . '">&laquo; ' . esc_html__('Previous', 'mxchat') . '</button>';
1269 1392 }
1270 - } else {
1271 - // Fallback to WordPress DB storage
1272 - $this->store_in_wordpress_db($content, $url, $embedding_vector);
1273 - }
1274 -}
1275 -// Modified storage function to add type field
1276 -private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) {
1277 - $vector_id = $vector_id ?: md5($url);
1278 - $options = get_option('mxchat_pinecone_addon_options');
1279 - $host = $options['mxchat_pinecone_host'] ?? '';
1280 1393
1281 - if (empty($host)) {
1282 - return array(
1283 - 'success' => false,
1284 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
1285 - );
1286 - }
1394 + $start_page = max(1, $page - 2);
1395 + $end_page = min($total_pages, $page + 2);
1287 1396
1288 - // Determine if this is a product URL
1289 - $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false);
1397 + if ($start_page > 1) {
1398 + echo '<button class="mxchat-pagination-button" data-page="1">1</button>';
1399 + if ($start_page > 2) {
1400 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1401 + }
1402 + }
1290 1403
1291 - $api_endpoint = "https://{$host}/vectors/upsert";
1292 - $request_body = array(
1293 - 'vectors' => array(
1294 - array(
1295 - 'id' => $vector_id,
1296 - 'values' => $embedding_vector,
1297 - 'metadata' => array(
1298 - 'text' => $content,
1299 - 'source_url' => $url,
1300 - 'type' => $is_product ? 'product' : 'content',
1301 - 'last_updated' => time()
1302 - )
1303 - )
1304 - )
1305 - );
1404 + for ($i = $start_page; $i <= $end_page; $i++) {
1405 + $class = $i === $page ? 'mxchat-pagination-button active' : 'mxchat-pagination-button';
1406 + echo '<button class="' . $class . '" data-page="' . $i . '">' . $i . '</button>';
1407 + }
1306 1408
1307 - $response = wp_remote_post($api_endpoint, array(
1308 - 'headers' => array(
1309 - 'Api-Key' => $api_key,
1310 - 'accept' => 'application/json',
1311 - 'content-type' => 'application/json'
1312 - ),
1313 - 'body' => wp_json_encode($request_body),
1314 - 'timeout' => 30,
1315 - 'data_format' => 'body'
1316 - ));
1409 + if ($end_page < $total_pages) {
1410 + if ($end_page < $total_pages - 1) {
1411 + echo '<span class="mxchat-pagination-ellipsis">...</span>';
1412 + }
1413 + echo '<button class="mxchat-pagination-button" data-page="' . $total_pages . '">' . $total_pages . '</button>';
1414 + }
1317 1415
1318 - if (is_wp_error($response)) {
1319 - return array(
1320 - 'success' => false,
1321 - 'message' => $response->get_error_message()
1322 - );
1416 + if ($page < $total_pages) {
1417 + echo '<button class="mxchat-pagination-button" data-page="' . ($page + 1) . '">' . esc_html__('Next', 'mxchat') . ' &raquo;</button>';
1418 + }
1419 +
1420 + echo '</div>';
1323 1421 }
1324 1422
1325 - $response_code = wp_remote_retrieve_response_code($response);
1326 - if ($response_code !== 200) {
1327 - $body = wp_remote_retrieve_body($response);
1328 - return array(
1329 - 'success' => false,
1330 - 'message' => sprintf(
1331 - 'Pinecone API error (HTTP %d): %s',
1332 - $response_code,
1333 - $body
1334 - )
1335 - );
1336 - }
1423 + echo '</div>';
1337 1424
1338 - return array(
1339 - 'success' => true,
1340 - 'message' => 'Successfully stored in Pinecone'
1341 - );
1425 + wp_send_json([
1426 + 'html' => ob_get_clean(),
1427 + 'page' => $page,
1428 + 'total_pages' => $total_pages,
1429 + 'total_sessions' => $total_sessions
1430 + ]);
1431 +
1432 + wp_die();
1342 1433 }
1343 -// Helper method for WordPress DB storage
1344 -private function store_in_wordpress_db($content, $url, $embedding_vector) {
1345 - global $wpdb;
1346 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1347 1434
1348 - $existing_entry = $wpdb->get_row(
1349 - $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url)
1350 - );
1351 1435
1352 - if ($existing_entry) {
1353 - $wpdb->update(
1354 - $table_name,
1355 - array(
1356 - 'article_content' => $content,
1357 - 'embedding_vector' => serialize($embedding_vector),
1358 - 'timestamp' => current_time('mysql')
1359 - ),
1360 - array('id' => $existing_entry->id),
1361 - array('%s', '%s', '%s')
1436 +/**
1437 + * ALTERNATIVE: Simpler helper method using string replacement
1438 + */
1439 +private function highlight_clicked_links($message_content, $clicked_urls) {
1440 + if (empty($clicked_urls)) {
1441 + return wp_kses(
1442 + $message_content,
1443 + [
1444 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1445 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1446 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1447 + ]
1362 1448 );
1363 - } else {
1364 - $wpdb->insert(
1365 - $table_name,
1366 - array(
1367 - 'article_content' => $content,
1368 - 'source_url' => $url,
1369 - 'embedding_vector' => serialize($embedding_vector),
1370 - 'timestamp' => current_time('mysql')
1371 - ),
1372 - array('%s', '%s', '%s', '%s')
1373 - );
1374 1449 }
1375 -}
1376 -/**
1377 - * Handle deletion of posts and pages from both Pinecone and WordPress DB
1378 - *
1379 - * @param int $post_id The ID of the post being deleted
1380 - * @return void
1381 - */
1382 -public function mxchat_handle_post_delete($post_id) {
1383 - // Get post data before it's deleted
1384 - $post = get_post($post_id);
1385 1450
1386 - // Basic validation
1387 - if (!$post || wp_is_post_revision($post_id)) {
1388 - return;
1389 - }
1451 + // First apply standard sanitization
1452 + $message_content = wp_kses(
1453 + $message_content,
1454 + [
1455 + 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1456 + 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1457 + 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1458 + ]
1459 + );
1390 1460
1391 - // Check post type
1392 - $post_type = $post->post_type;
1393 - if (!in_array($post_type, array('post', 'page'))) {
1394 - return;
1395 - }
1461 + // Process each clicked URL
1462 + foreach ($clicked_urls as $clicked_url) {
1463 + // Try multiple patterns to catch different link formats
1464 + $patterns = [
1465 + // Standard link format
1466 + '/<a([^>]*href=["\']' . preg_quote($clicked_url, '/') . '["\'][^>]*)>/i',
1467 + // Link with trailing slash
1468 + '/<a([^>]*href=["\']' . preg_quote(rtrim($clicked_url, '/'), '/') . '\/?["\'][^>]*)>/i',
1469 + // Encoded entities version
1470 + '/<a([^>]*href=["\']' . preg_quote(htmlentities($clicked_url), '/') . '["\'][^>]*)>/i',
1471 + ];
1396 1472
1397 - // Check if sync is enabled for this post type
1398 - $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages';
1399 - if (get_option($sync_option) != '1') {
1400 - return;
1401 - }
1473 + foreach ($patterns as $pattern) {
1474 + if (preg_match($pattern, $message_content)) {
1475 + $message_content = preg_replace_callback(
1476 + $pattern,
1477 + function($matches) {
1478 + $full_match = $matches[0];
1479 + $attributes = $matches[1];
1402 1480
1403 - // Get the URL before post is deleted
1404 - $source_url = get_permalink($post_id);
1405 - if (!$source_url) {
1406 - //error_log('MXChat: Failed to get permalink for post ' . $post_id);
1407 - return;
1408 - }
1481 + // Check if it already has the class
1482 + if (strpos($full_match, 'mxchat-clicked-link') !== false) {
1483 + return $full_match;
1484 + }
1409 1485
1410 - // Check for Pinecone addon and its settings
1411 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
1412 - $use_pinecone = false;
1413 - if ($pinecone_settings && is_array($pinecone_settings)) {
1414 - $use_pinecone = (
1415 - isset($pinecone_settings['mxchat_use_pinecone']) &&
1416 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
1417 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
1418 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
1419 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
1420 - !empty($pinecone_settings['mxchat_pinecone_environment'])
1421 - );
1422 - }
1486 + // Check if class attribute exists
1487 + if (preg_match('/class=["\']([^"\']*)["\']/', $attributes, $class_matches)) {
1488 + // Add to existing class
1489 + $new_attributes = preg_replace(
1490 + '/class=["\']([^"\']*)["\']/',
1491 + 'class="$1 mxchat-clicked-link"',
1492 + $attributes
1493 + );
1494 + } else {
1495 + // Add new class attribute
1496 + $new_attributes = $attributes . ' class="mxchat-clicked-link"';
1497 + }
1423 1498
1424 - $deletion_successful = false;
1499 + // Add title if not present
1500 + if (strpos($new_attributes, 'title=') === false) {
1501 + $new_attributes .= ' title="User clicked this link"';
1502 + }
1425 1503
1426 - if ($use_pinecone) {
1427 - try {
1428 - $pinecone_result = $this->delete_from_pinecone(
1429 - array($source_url),
1430 - $pinecone_settings['mxchat_pinecone_api_key'],
1431 - $pinecone_settings['mxchat_pinecone_environment'],
1432 - $pinecone_settings['mxchat_pinecone_index']
1433 - );
1504 + return '<a' . $new_attributes . '>';
1505 + },
1506 + $message_content
1507 + );
1434 1508
1435 - if (!$pinecone_result['success']) {
1436 - //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']);
1437 - } else {
1438 - $deletion_successful = true;
1509 + // If we found and replaced, break out of the patterns loop
1510 + break;
1439 1511 }
1440 - } catch (Exception $e) {
1441 - //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage());
1442 1512 }
1443 1513 }
1444 1514
1445 - // Always attempt WordPress DB deletion, regardless of Pinecone status
1446 - try {
1447 - global $wpdb;
1448 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1515 + return $message_content;
1516 +}
1449 1517
1450 - $result = $wpdb->delete(
1451 - $table_name,
1452 - array('source_url' => $source_url),
1453 - array('%s')
1454 - );
1518 +public function mxchat_create_prompts_page() {
1519 + //error_log('=== DEBUG: mxchat_create_prompts_page started ===');
1455 1520
1456 - if ($result === false) {
1457 - //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url);
1458 - } else {
1459 - $deletion_successful = true;
1460 - }
1461 - } catch (Exception $e) {
1462 - //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage());
1463 - }
1521 + global $wpdb;
1522 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1464 1523
1465 - if (!$deletion_successful) {
1466 - //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url);
1524 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1525 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
1526 +
1527 + // Display success message if all prompts were deleted
1528 + if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
1529 + echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
1467 1530 }
1468 -}
1469 1531
1532 + // Set up pagination and search query
1533 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
1534 + $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
1535 + $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
1536 + $per_page = 10;
1470 1537
1471 -public function mxchat_handle_content_submission() {
1472 - // Check if the form was submitted and the user has permission.
1473 - if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) {
1474 - return;
1475 - }
1538 + //error_log('DEBUG: Search query: ' . $search_query);
1539 + //error_log('DEBUG: Current page: ' . $current_page);
1540 + //error_log('DEBUG: Per page: ' . $per_page);
1476 1541
1477 - // Verify the nonce.
1478 - $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : '';
1479 - if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) {
1480 - wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
1481 - }
1542 + // ================================
1543 + // REPLACE THIS SECTION WITH UNIFIED TABLE LOGIC
1544 + // ================================
1482 1545
1483 - // Sanitize the inputs.
1484 - $article_content = sanitize_textarea_field($_POST['article_content']);
1485 - $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : '';
1546 + // Get Pinecone settings to determine data source
1547 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
1548 + //error_log('DEBUG: Pinecone options retrieved: ' . print_r($pinecone_options, true));
1486 1549
1487 - // Generate the embedding vector.
1488 - $embedding_vector = $this->mxchat_generate_embedding($article_content);
1489 - if (!is_array($embedding_vector)) {
1490 - set_transient('mxchat_admin_notice_error',
1491 - esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'),
1492 - 30
1493 - );
1494 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1495 - exit;
1496 - }
1550 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
1551 + //error_log('DEBUG: use_pinecone decision: ' . ($use_pinecone ? 'TRUE' : 'FALSE'));
1497 1552
1498 - // Store in the WordPress database.
1499 - global $wpdb;
1500 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1553 + $pinecone_api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
1554 + //error_log('DEBUG: Pinecone API key present: ' . (!empty($pinecone_api_key) ? 'YES' : 'NO'));
1501 1555
1502 - // Ensure the source_url column exists.
1503 - if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') {
1504 - $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''"));
1505 - }
1556 + if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
1557 + //error_log('DEBUG: Using PINECONE data source');
1558 + // PINECONE DATA SOURCE
1559 + $data_source = 'pinecone';
1560 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
1561 + //error_log('DEBUG: About to call mxchat_fetch_pinecone_records');
1506 1562
1507 - // Serialize and store the embedding vector.
1508 - $embedding_vector_serialized = serialize($embedding_vector);
1509 - $inserted = $wpdb->insert(
1510 - $table_name,
1511 - array(
1512 - 'article_content' => $article_content,
1513 - 'embedding_vector' => $embedding_vector_serialized,
1514 - 'source_url' => $article_url,
1515 - ),
1516 - array('%s', '%s', '%s')
1517 - );
1563 + $records = $pinecone_manager->mxchat_fetch_pinecone_records($pinecone_options, $search_query, $current_page, $per_page);
1564 + $total_records = $records['total'] ?? 0;
1565 + $prompts = $records['data'] ?? array();
1566 + $total_in_database = $records['total_in_database'] ?? 0;
1567 + $showing_recent_only = $records['showing_recent_only'] ?? false;
1518 1568
1519 - if ($inserted === false) {
1520 - set_transient('mxchat_admin_notice_error',
1521 - esc_html__('Error inserting content into the database. Please try again.', 'mxchat'),
1522 - 30
1523 - );
1524 - } else {
1525 - set_transient('mxchat_admin_notice_success',
1526 - esc_html__('Content successfully submitted!', 'mxchat'),
1527 - 30
1528 - );
1529 - }
1569 + $total_pages = ceil($total_records / $per_page);
1530 1570
1531 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1532 - exit;
1533 -}
1571 + //error_log('DEBUG: Pinecone - total_records: ' . $total_records);
1572 + //error_log('DEBUG: Pinecone - prompts count: ' . count($prompts));
1573 + //error_log('DEBUG: Pinecone - total_pages: ' . $total_pages);
1574 + //error_log('DEBUG: Pinecone - showing_recent_only: ' . ($showing_recent_only ? 'TRUE' : 'FALSE'));
1575 + //error_log('DEBUG: Pinecone - total_in_database: ' . $total_in_database);
1576 +} else {
1577 + //error_log('DEBUG: Using WORDPRESS DB data source');
1578 + // WORDPRESS DB DATA SOURCE (your existing logic)
1579 + $data_source = 'wordpress';
1534 1580
1535 -private function is_pdf_url($url, $response) {
1536 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1537 - $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
1581 + // Initialize these variables for WordPress DB
1582 + $total_in_database = 0;
1583 + $showing_recent_only = false;
1538 1584
1539 - return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf';
1540 -}
1541 -private function handle_pdf_for_knowledge_base($pdf_url, $response) {
1542 - if (!current_user_can('manage_options')) {
1543 - //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat'));
1544 - return false;
1545 - }
1585 + $offset = ($current_page - 1) * $per_page;
1546 1586
1547 - $pdf_url = esc_url_raw($pdf_url);
1548 - $upload_dir = wp_upload_dir();
1549 -
1550 - if (isset($upload_dir['error']) && $upload_dir['error'] !== false) {
1551 - //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error'])));
1552 - return false;
1587 + // Modify query to handle search input
1588 + $sql_search = "";
1589 + if ($search_query) {
1590 + $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%');
1553 1591 }
1554 1592
1555 - $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf');
1556 - $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename;
1593 + // Retrieve total number of prompts, considering search filter
1594 + $count_query = "SELECT COUNT(*) FROM {$table_name} {$sql_search}";
1595 + //error_log('DEBUG: WordPress count query: ' . $count_query);
1557 1596
1558 - $response_body = wp_remote_retrieve_body($response);
1559 - if (empty($response_body)) {
1560 - //error_log(esc_html__('Empty PDF response body', 'mxchat'));
1561 - return false;
1562 - }
1597 + $total_records = $wpdb->get_var($count_query);
1598 + //error_log('DEBUG: WordPress total_records: ' . $total_records);
1563 1599
1564 - if (!wp_mkdir_p(dirname($pdf_path))) {
1565 - //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path)));
1566 - return false;
1567 - }
1600 + $total_pages = ceil($total_records / $per_page);
1568 1601
1569 - try {
1570 - file_put_contents($pdf_path, $response_body);
1602 + // Retrieve prompts from the database
1603 + $prompts_query = $wpdb->prepare(
1604 + "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
1605 + $per_page,
1606 + $offset
1607 + );
1608 + //error_log('DEBUG: WordPress prompts query: ' . $prompts_query);
1571 1609
1572 - if (!file_exists($pdf_path)) {
1573 - throw new Exception(__('Failed to save PDF file', 'mxchat'));
1574 - }
1610 + $prompts = $wpdb->get_results($prompts_query);
1611 + //error_log('DEBUG: WordPress prompts count: ' . count($prompts));
1612 +}
1575 1613
1576 - $parser = new \Smalot\PdfParser\Parser();
1577 - $pdf = $parser->parseFile($pdf_path);
1578 - $total_pages = absint(count($pdf->getPages()));
1614 +//error_log('DEBUG: Final data_source: ' . $data_source);
1615 +//error_log('DEBUG: Final total_records: ' . $total_records);
1616 +//error_log('DEBUG: Final prompts count: ' . count($prompts));
1617 +//error_log('DEBUG: Final showing_recent_only: ' . ($showing_recent_only ? 'TRUE' : 'FALSE'));
1618 +//error_log('DEBUG: Final total_in_database: ' . $total_in_database);
1579 1619
1580 - if ($total_pages < 1) {
1581 - throw new Exception(__('Invalid PDF: no pages found', 'mxchat'));
1582 - }
1583 -
1584 - wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array(
1585 - 'pdf_path' => $pdf_path,
1586 - 'pdf_url' => $pdf_url,
1587 - 'total_pages' => $total_pages,
1588 - 'batch_size' => absint(15),
1589 - 'batch_pause' => absint(10)
1620 + // Add the pagination links generation here
1621 + $page_links = '';
1622 + if ($total_pages > 1) {
1623 + $page_links = paginate_links(array(
1624 + 'base' => add_query_arg(array(
1625 + 'paged' => '%#%',
1626 + 'search' => urlencode($search_query),
1627 + '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce')
1628 + ), admin_url('admin.php?page=mxchat-prompts')),
1629 + 'format' => '',
1630 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
1631 + 'next_text' => __('Next &raquo;', 'mxchat'),
1632 + 'total' => $total_pages,
1633 + 'current' => $current_page,
1590 1634 ));
1591 -
1592 - $status_data = array(
1593 - 'total_pages' => $total_pages,
1594 - 'processed_pages' => 0,
1595 - 'status' => 'processing',
1596 - 'last_update' => time()
1597 - );
1598 -
1599 - set_transient(
1600 - sanitize_key('mxchat_pdf_status_' . md5($pdf_url)),
1601 - array_map('sanitize_text_field', $status_data),
1602 - DAY_IN_SECONDS
1603 - );
1604 -
1605 - return __('scheduled', 'mxchat');
1606 -
1607 - } catch (Exception $e) {
1608 - //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1609 - if (file_exists($pdf_path)) {
1610 - wp_delete_file($pdf_path);
1611 - }
1612 - return false;
1613 1635 }
1614 -}
1615 -public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) {
1616 - // Validate inputs
1617 - $pdf_path = sanitize_text_field($pdf_path);
1618 - $pdf_url = esc_url_raw($pdf_url);
1619 - $total_pages = absint($total_pages);
1620 - $batch_size = absint($batch_size);
1621 - $batch_pause = absint($batch_pause);
1622 1636
1623 - try {
1624 - if (!file_exists($pdf_path)) {
1625 - throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path)));
1626 - }
1637 + // ================================
1638 + // END OF REPLACEMENT SECTION
1639 + // ================================
1627 1640
1628 - $parser = new \Smalot\PdfParser\Parser();
1629 - $pdf = $parser->parseFile($pdf_path);
1630 - $pages = $pdf->getPages();
1641 + // Retrieve processing statuses
1642 + $pdf_url = get_transient('mxchat_last_pdf_url');
1643 + $sitemap_url = get_transient('mxchat_last_sitemap_url');
1644 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1645 + $pdf_status = $pdf_url ? $knowledge_manager->mxchat_get_pdf_processing_status($pdf_url) : false;
1646 + $sitemap_status = $sitemap_url ? $knowledge_manager->mxchat_get_sitemap_processing_status($sitemap_url) : false;
1631 1647
1632 - // Get current progress
1633 - $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url));
1634 - $status = get_transient($status_key);
1648 + $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
1649 + || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
1635 1650
1636 - if (!$status || !is_array($status)) {
1637 - throw new Exception('Invalid status data retrieved from transient');
1638 - }
1651 + //error_log('=== DEBUG: mxchat_create_prompts_page data preparation completed ===');
1639 1652
1640 - $start_page = absint($status['processed_pages']);
1641 - $end_page = min($start_page + $batch_size, $total_pages);
1653 + ?>
1642 1654
1643 - $instance = new self(); // Create an instance of the class
1655 +<div class="wrap mxchat-wrapper">
1656 + <!-- Hero Section -->
1657 + <div class="mxchat-hero">
1658 + <h1 class="mxchat-main-title">
1659 + <span class="mxchat-gradient-text">Knowledge Base</span> Manager
1660 + </h1>
1661 + <p class="mxchat-hero-subtitle">
1662 + <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
1663 + </p>
1664 + </div>
1644 1665
1645 - for ($i = $start_page; $i < $end_page; $i++) {
1646 - $text = $pages[$i]->getText();
1647 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance
1666 + <div class="mxchat-content">
1648 1667
1649 - if (!empty($sanitized_content)) {
1650 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance
1651 - if (is_array($embedding_vector)) {
1652 - $metadata = array(
1653 - 'document_type' => 'pdf',
1654 - 'total_pages' => $total_pages,
1655 - 'current_page' => $i + 1,
1656 - 'prev_page' => $i > 0 ? $i : null,
1657 - 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null,
1658 - 'source_url' => $pdf_url
1659 - );
1660 1668
1661 - $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content;
1662 - $page_url = esc_url($pdf_url . "#page=" . ($i + 1));
1669 +<!-- Tab Navigation -->
1670 +<div class="mxchat-kb-tabs-nav">
1671 + <button class="mxchat-kb-tab-button active" data-tab="import">
1672 + <?php esc_html_e('Knowledge Import', 'mxchat'); ?>
1673 + </button>
1674 + <button class="mxchat-kb-tab-button" data-tab="sync">
1675 + <?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?>
1676 + </button>
1677 + <button class="mxchat-kb-tab-button" data-tab="pinecone">
1678 + <?php esc_html_e('Pinecone Settings', 'mxchat'); ?>
1679 + </button>
1680 +</div>
1663 1681
1664 - $options = get_option('mxchat_options');
1665 - MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']);
1666 - }
1667 - }
1668 1682
1669 - // Update progress with sanitized data
1670 - $status['processed_pages'] = absint($i + 1);
1671 - $status['last_update'] = time();
1672 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1673 - }
1683 +<div class="mxchat-kb-tabs-content">
1684 + <div id="mxchat-kb-tab-import" class="mxchat-kb-tab-content active">
1685 +<!-- Import Options Card -->
1686 +<div class="mxchat-card">
1674 1687
1675 - // Schedule next batch if needed
1676 - if ($end_page < $total_pages) {
1677 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array(
1678 - 'pdf_path' => $pdf_path,
1679 - 'pdf_url' => $pdf_url,
1680 - 'total_pages' => $total_pages,
1681 - 'batch_size' => $batch_size,
1682 - 'batch_pause' => $batch_pause
1683 - ));
1684 - } else {
1685 - // Processing complete
1686 - $status['status'] = 'complete';
1687 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1688 - if (file_exists($pdf_path)) {
1689 - wp_delete_file($pdf_path);
1690 - }
1691 - }
1688 + <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
1692 1689
1693 - } catch (\Exception $e) {
1694 - $status['status'] = 'error';
1695 - $status['error'] = sanitize_text_field($e->getMessage());
1696 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1697 - if (file_exists($pdf_path)) {
1698 - wp_delete_file($pdf_path);
1699 - }
1700 - }
1701 -}
1702 -public function get_pdf_processing_status($pdf_url) {
1703 - $pdf_url = esc_url_raw($pdf_url);
1704 - $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url)));
1690 +<?php
1691 +// Check if the appropriate embedding API key exists
1692 +$embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
1693 +$has_openai_key = !empty($this->options['api_key']);
1694 +$has_voyage_key = !empty($this->options['voyage_api_key']);
1695 +$has_gemini_key = !empty($this->options['gemini_api_key']);
1705 1696
1706 - if (!$status || !is_array($status)) {
1707 - return false;
1708 - }
1697 +// Determine if they have the needed API key for their selected embedding model
1698 +$has_required_key = false;
1699 +$required_key_type = '';
1709 1700
1710 - return array(
1711 - 'total_pages' => absint($status['total_pages']),
1712 - 'processed_pages' => absint($status['processed_pages']),
1713 - 'percentage' => ($status['total_pages'] > 0)
1714 - ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100)
1715 - : 0,
1716 - 'status' => sanitize_text_field($status['status']),
1717 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat')
1718 - );
1701 +if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
1702 + $has_required_key = true;
1703 + $required_key_type = 'OpenAI';
1704 +} elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
1705 + $has_required_key = true;
1706 + $required_key_type = 'Voyage AI';
1707 +} elseif (strpos($embedding_model, 'gemini-embedding-') !== false && $has_gemini_key) {
1708 + $has_required_key = true;
1709 + $required_key_type = 'Google Gemini';
1710 +} elseif (strpos($embedding_model, 'text-embedding-') !== false) {
1711 + $required_key_type = 'OpenAI';
1712 +} elseif (strpos($embedding_model, 'voyage-') !== false) {
1713 + $required_key_type = 'Voyage AI';
1714 +} elseif (strpos($embedding_model, 'gemini-embedding-') !== false) {
1715 + $required_key_type = 'Google Gemini';
1719 1716 }
1720 -public function mxchat_handle_sitemap_submission() {
1721 - // Check if the form was submitted and verify permissions
1722 - if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) {
1723 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
1724 - }
1717 +?>
1725 1718
1726 - // Verify nonce
1727 - check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce');
1719 + <div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
1720 + <?php if ($has_required_key): ?>
1721 + <p><span class="dashicons dashicons-yes-alt"></span> <?php echo wp_kses_post(sprintf(__('We detected your %s API key. <strong>Remember to add credits to your %s account</strong> before using the knowledgebase.', 'mxchat'), $required_key_type, $required_key_type)); ?></p>
1722 + <?php else: ?>
1723 + <p><span class="dashicons dashicons-warning"></span> <strong><?php esc_html_e('Important:', 'mxchat'); ?></strong> <?php echo sprintf(esc_html__('Before importing knowledge, you must add a %s API key with sufficient credits in the Chatbot settings.', 'mxchat'), $required_key_type); ?> <a href="<?php echo admin_url('admin.php?page=mxchat-max'); ?>"><?php esc_html_e('Go to API Key Settings', 'mxchat'); ?></a></p>
1724 + <?php endif; ?>
1725 + </div>
1728 1726
1729 - // Validate URL
1730 - if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) {
1731 - set_transient('mxchat_admin_notice_error',
1732 - esc_html__('Please provide a valid URL.', 'mxchat'),
1733 - 30
1734 - );
1735 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1736 - exit;
1737 - }
1738 1727
1739 - $submitted_url = esc_url_raw($_POST['sitemap_url']);
1740 - $response = wp_remote_get($submitted_url, array('timeout' => 30));
1741 1728
1742 - if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
1743 - $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat');
1744 - set_transient('mxchat_admin_notice_error',
1745 - sprintf(
1746 - esc_html__('Failed to fetch the URL: %s', 'mxchat'),
1747 - esc_html($error_message)
1748 - ),
1749 - 30
1750 - );
1751 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1752 - exit;
1753 - }
1729 +<!-- Import Options Section -->
1730 + <div class="mxchat-import-section">
1731 + <h3><?php esc_html_e('Import Options', 'mxchat'); ?></h3>
1754 1732
1755 - $content_type = wp_remote_retrieve_header($response, 'content-type');
1756 - $body_content = wp_remote_retrieve_body($response);
1733 + <!-- Import Options Grid -->
1734 + <div class="mxchat-import-options">
1735 + <!-- WordPress Import Option -->
1736 + <button type="button" id="mxchat-open-content-selector" class="mxchat-import-box mxchat-import-wordpress" data-option="wordpress">
1737 + <div class="mxchat-import-icon">
1738 + <span class="dashicons dashicons-wordpress"></span>
1739 + </div>
1740 + <div class="mxchat-import-content">
1741 + <h4><?php esc_html_e('WordPress Content', 'mxchat'); ?></h4>
1742 + <p><?php esc_html_e('Import specific posts and pages to your knowledge base.', 'mxchat'); ?></p>
1743 + </div>
1744 + <div class="mxchat-recommended-tag"><?php esc_html_e('Recommended', 'mxchat'); ?></div>
1745 + </button>
1757 1746
1758 - if (empty($body_content)) {
1759 - set_transient('mxchat_admin_notice_error',
1760 - esc_html__('Empty response received from URL.', 'mxchat'),
1761 - 30
1762 - );
1763 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1764 - exit;
1765 - }
1747 + <!-- Sitemap Import Option -->
1748 + <button type="button" class="mxchat-import-box" data-option="sitemap" data-placeholder="<?php esc_attr_e('Enter sitemap URL here', 'mxchat'); ?>" data-type="sitemap">
1749 + <div class="mxchat-import-icon">
1750 + <span class="dashicons dashicons-admin-site-alt"></span>
1751 + </div>
1752 + <div class="mxchat-import-content">
1753 + <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
1754 + <p><?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
1755 + </div>
1756 + </button>
1766 1757
1767 - // Handle PDF URL
1768 - if ($this->is_pdf_url($submitted_url, $response)) {
1769 - $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response);
1758 + <!-- Direct URL Import Option -->
1759 + <button type="button" class="mxchat-import-box" data-option="url" data-placeholder="<?php esc_attr_e('Enter webpage URL here', 'mxchat'); ?>" data-type="url">
1760 + <div class="mxchat-import-icon">
1761 + <span class="dashicons dashicons-admin-links"></span>
1762 + </div>
1763 + <div class="mxchat-import-content">
1764 + <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
1765 + <p><?php esc_html_e('Import content from any webpage.', 'mxchat'); ?></p>
1766 + </div>
1767 + </button>
1770 1768
1771 - if ($result === 'scheduled') {
1772 - set_transient(
1773 - 'mxchat_last_pdf_url',
1774 - sanitize_text_field($submitted_url),
1775 - DAY_IN_SECONDS
1776 - );
1777 - set_transient('mxchat_admin_notice_info',
1778 - esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1779 - 30
1780 - );
1781 - } else {
1782 - set_transient('mxchat_admin_notice_error',
1783 - esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'),
1784 - 30
1785 - );
1786 - }
1769 + <!-- Direct Content Import Option -->
1770 + <button type="button" class="mxchat-import-box" data-option="content" data-type="content">
1771 + <div class="mxchat-import-icon">
1772 + <span class="dashicons dashicons-editor-paste-text"></span>
1773 + </div>
1774 + <div class="mxchat-import-content">
1775 + <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
1776 + <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p>
1777 + </div>
1778 + </button>
1787 1779
1788 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1789 - exit;
1790 - }
1780 + <!-- PDF Import Option -->
1781 + <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf">
1782 + <div class="mxchat-import-icon">
1783 + <span class="dashicons dashicons-media-document"></span>
1784 + </div>
1785 + <div class="mxchat-import-content">
1786 + <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
1787 + <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p>
1788 + </div>
1789 + </button>
1790 + </div>
1791 1791
1792 - // Handle Sitemap XML
1793 - if (strpos($content_type, 'xml') !== false || strpos($body_content, '<urlset') !== false) {
1794 - libxml_use_internal_errors(true);
1795 - $xml = simplexml_load_string($body_content);
1796 - $xml_errors = libxml_get_errors();
1797 - libxml_clear_errors();
1792 + <!-- Input Areas for URL and Content -->
1793 + <div class="mxchat-import-input-area" id="mxchat-url-input-area" style="display: none;">
1794 + <?php if (!$is_processing) : ?>
1795 + <form id="mxchat-url-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
1796 + <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
1797 + <input type="hidden" name="import_type" id="import_type" value="url">
1798 + <div class="mxchat-url-input-group">
1799 + <input type="url"
1800 + name="sitemap_url"
1801 + id="sitemap_url"
1802 + placeholder="<?php esc_attr_e('Enter URL here', 'mxchat'); ?>"
1803 + required />
1804 + <button type="submit"
1805 + name="submit_sitemap"
1806 + class="mxchat-button-primary">
1807 + <?php esc_html_e('Import', 'mxchat'); ?>
1808 + </button>
1809 + </div>
1810 + <p class="mxchat-url-description" id="url-description-text"></p>
1811 + </form>
1812 + <?php endif; ?>
1813 + </div>
1798 1814
1799 - if ($xml === false || !empty($xml_errors)) {
1800 - set_transient('mxchat_admin_notice_error',
1801 - esc_html__('Invalid sitemap XML. Please provide a valid sitemap.', 'mxchat'),
1802 - 30
1803 - );
1804 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1805 - exit;
1806 - }
1815 + <div class="mxchat-import-input-area" id="mxchat-content-input-area" style="display: none;">
1816 + <?php if (!$is_processing) : ?>
1817 + <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
1818 + <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
1819 + <div class="mxchat-form-group">
1820 + <textarea
1821 + name="article_content"
1822 + id="article_content"
1823 + placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"
1824 + required
1825 + rows="6"
1826 + ></textarea>
1827 + </div>
1828 + <div class="mxchat-form-group">
1829 + <input type="url"
1830 + name="article_url"
1831 + id="article_url"
1832 + placeholder="<?php esc_attr_e('Enter source URL (Optional)', 'mxchat'); ?>">
1833 + </div>
1834 + <button type="submit"
1835 + name="submit_content"
1836 + class="mxchat-button-primary">
1837 + <?php esc_html_e('Import Content', 'mxchat'); ?>
1838 + </button>
1839 + </form>
1840 + <?php endif; ?>
1841 + </div>
1842 + </div>
1807 1843
1808 - $result = $this->handle_sitemap_for_knowledge_base($xml, $submitted_url);
1844 +<!-- PDF Processing Status - ONLY PROCESSING -->
1845 + <?php if ($pdf_status && $pdf_status['status'] === 'processing') : ?>
1846 + <div class="mxchat-status-card" data-card-type="pdf">
1847 + <div class="mxchat-status-header">
1848 + <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
1809 1849
1810 - if ($result === 'scheduled') {
1811 - set_transient(
1812 - 'mxchat_last_sitemap_url',
1813 - sanitize_text_field($submitted_url),
1814 - DAY_IN_SECONDS
1815 - );
1816 - set_transient('mxchat_admin_notice_info',
1817 - esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'),
1818 - 30
1819 - );
1820 - } else {
1821 - set_transient('mxchat_admin_notice_error',
1822 - esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'),
1823 - 30
1824 - );
1825 - }
1850 + <!-- Processing controls -->
1851 + <div class="mxchat-processing-controls">
1852 + <form method="post" class="mxchat-stop-form"
1853 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1854 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1855 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1856 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1857 + </button>
1858 + </form>
1826 1859
1827 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1828 - exit;
1829 - }
1860 + <button type="button" class="mxchat-manual-batch-btn"
1861 + data-process-type="pdf"
1862 + data-url="<?php echo esc_attr(get_transient('mxchat_last_pdf_url')); ?>">
1863 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
1864 + </button>
1865 + </div>
1866 + </div>
1830 1867
1831 - // Handle Regular URL
1832 - $page_content = $this->mxchat_extract_main_content($body_content);
1833 - $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content);
1868 + <div class="mxchat-progress-bar">
1869 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
1870 + </div>
1834 1871
1835 - if (empty($sanitized_content)) {
1836 - set_transient('mxchat_admin_notice_error',
1837 - esc_html__('No valid content found on the provided URL.', 'mxchat'),
1838 - 30
1839 - );
1840 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1841 - exit;
1842 - }
1872 + <div class="mxchat-status-details">
1873 + <p><?php printf(
1874 + esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
1875 + absint($pdf_status['processed_pages']),
1876 + absint($pdf_status['total_pages']),
1877 + absint($pdf_status['percentage'])
1878 + ); ?></p>
1843 1879
1844 - $embedding_vector = $this->mxchat_generate_embedding($sanitized_content);
1845 - if (is_array($embedding_vector)) {
1846 - MxChat_Utils::submit_content_to_db(
1847 - $sanitized_content,
1848 - $submitted_url,
1849 - $this->options['api_key']
1850 - );
1851 - set_transient('mxchat_admin_notice_success',
1852 - esc_html__('URL content successfully submitted!', 'mxchat'),
1853 - 30
1854 - );
1855 - } else {
1856 - set_transient('mxchat_admin_notice_error',
1857 - esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'),
1858 - 30
1859 - );
1860 - }
1880 + <?php if (!empty($pdf_status['failed_pages']) && $pdf_status['failed_pages'] > 0) : ?>
1881 + <p><strong><?php esc_html_e('Failed pages:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['failed_pages']); ?></p>
1882 + <?php endif; ?>
1861 1883
1862 - wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts')));
1863 - exit;
1864 -}
1865 -private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) {
1866 - if (!current_user_can('manage_options')) {
1867 - //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat'));
1868 - return false;
1869 - }
1884 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($pdf_status['status'])); ?></p>
1885 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['last_update']); ?></p>
1886 + </div>
1887 + </div>
1888 + <?php endif; ?>
1870 1889
1871 - try {
1872 - $sitemap_url = esc_url_raw($sitemap_url);
1890 + <!-- Sitemap Processing Status - ONLY PROCESSING -->
1891 + <?php if ($sitemap_status && $sitemap_status['status'] === 'processing') : ?>
1892 + <div class="mxchat-status-card" data-card-type="sitemap">
1893 + <div class="mxchat-status-header">
1894 + <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
1873 1895
1874 - if (!$xml || !is_object($xml)) {
1875 - throw new Exception(__('Invalid XML object provided', 'mxchat'));
1876 - }
1896 + <!-- Processing controls -->
1897 + <div class="mxchat-processing-controls">
1898 + <form method="post" class="mxchat-stop-form"
1899 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
1900 + <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
1901 + <button type="submit" name="stop_processing" class="mxchat-button-secondary">
1902 + <?php esc_html_e('Stop Processing', 'mxchat'); ?>
1903 + </button>
1904 + </form>
1877 1905
1878 - $urls = [];
1879 - foreach ($xml->url as $url_element) {
1880 - $url = esc_url_raw((string)$url_element->loc);
1881 - if ($url) {
1882 - $urls[] = $url;
1883 - }
1884 - }
1906 + <button type="button" class="mxchat-manual-batch-btn"
1907 + data-process-type="sitemap"
1908 + data-url="<?php echo esc_attr(get_transient('mxchat_last_sitemap_url')); ?>">
1909 + <?php esc_html_e('Process Batch', 'mxchat'); ?>
1910 + </button>
1911 + </div>
1912 + </div>
1885 1913
1886 - $total_urls = absint(count($urls));
1914 + <div class="mxchat-progress-bar">
1915 + <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
1916 + </div>
1887 1917
1888 - if ($total_urls < 1) {
1889 - throw new Exception(__('No valid URLs found in sitemap', 'mxchat'));
1890 - }
1918 + <div class="mxchat-status-details">
1919 + <p><?php printf(
1920 + esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
1921 + absint($sitemap_status['processed_urls']),
1922 + absint($sitemap_status['total_urls']),
1923 + absint($sitemap_status['percentage'])
1924 + ); ?></p>
1891 1925
1892 - wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array(
1893 - 'urls' => $urls,
1894 - 'sitemap_url' => $sitemap_url,
1895 - 'total_urls' => $total_urls,
1896 - 'batch_size' => absint(10),
1897 - 'batch_pause' => absint(5)
1898 - ));
1926 + <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
1927 + <p><strong><?php esc_html_e('Failed URLs:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['failed_urls']); ?></p>
1928 + <?php endif; ?>
1899 1929
1900 - $status_data = array(
1901 - 'total_urls' => $total_urls,
1902 - 'processed_urls' => 0,
1903 - 'status' => 'processing',
1904 - 'last_update' => time()
1905 - );
1930 + <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($sitemap_status['status'])); ?></p>
1931 + <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['last_update']); ?></p>
1906 1932
1907 - set_transient(
1908 - sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)),
1909 - array_map('sanitize_text_field', $status_data),
1910 - DAY_IN_SECONDS
1911 - );
1933 + <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
1934 + <div class="mxchat-error-notice">
1935 + <?php if (!empty($sitemap_status['error'])) : ?>
1936 + <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
1937 + <?php endif; ?>
1938 + <?php if (!empty($sitemap_status['last_error'])) : ?>
1939 + <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
1940 + <?php endif; ?>
1941 + </div>
1942 + <?php endif; ?>
1943 + </div>
1944 + </div>
1945 + <?php endif; ?>
1912 1946
1913 - return __('scheduled', 'mxchat');
1947 + <!-- Single URL Submission Status -->
1948 + <?php
1949 + // Get single URL status
1950 + $single_url_status = $this->knowledge_manager->mxchat_get_single_url_status();
1951 + $is_active_processing =
1952 + ($sitemap_status && $sitemap_status['status'] === 'processing') ||
1953 + ($pdf_status && $pdf_status['status'] === 'processing');
1954 + ?>
1914 1955
1915 - } catch (\Exception $e) {
1916 - //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage())));
1917 - return false;
1918 - }
1919 -}
1920 -public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) {
1921 - // Validate inputs
1922 - $sitemap_url = esc_url_raw($sitemap_url);
1923 - $total_urls = absint($total_urls);
1924 - $batch_size = absint($batch_size);
1925 - $batch_pause = absint($batch_pause);
1956 + <div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
1957 + <?php if ($single_url_status && !$is_active_processing) : ?>
1958 + <div class="mxchat-status-card">
1959 + <div class="mxchat-status-header">
1960 + <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
1961 + <?php if ($single_url_status['status'] === 'failed') : ?>
1962 + <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
1963 + <?php else : ?>
1964 + <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
1965 + <?php endif; ?>
1966 + </div>
1967 + <div class="mxchat-status-details">
1968 + <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
1969 + <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
1970 + <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
1971 + </a>
1972 + </p>
1973 + <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
1926 1974
1927 - if (!is_array($urls) || empty($urls)) {
1928 - return;
1929 - }
1975 + <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
1976 + <div class="mxchat-error-notice">
1977 + <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
1978 + </div>
1979 + <?php endif; ?>
1930 1980
1931 - try {
1932 - $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url));
1933 - $status = get_transient($status_key);
1981 + <?php if ($single_url_status['status'] === 'complete') : ?>
1982 + <p><strong><?php esc_html_e('Content Length:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['content_length']); ?> <?php esc_html_e('characters', 'mxchat'); ?></p>
1983 + <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
1984 + <?php endif; ?>
1985 + </div>
1986 + </div>
1987 + <?php endif; ?>
1988 + </div>
1934 1989
1935 - if (!$status || !is_array($status)) {
1936 - throw new Exception('Invalid status data retrieved from transient');
1937 - }
1990 + <!-- Completed Status Cards - Handles completed PDF/Sitemap processing -->
1991 + <?php
1992 + $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1993 + echo $knowledge_manager->mxchat_render_completed_status_cards();
1994 + ?>
1938 1995
1939 - $start_url = absint($status['processed_urls']);
1940 - $end_url = min($start_url + $batch_size, $total_urls);
1941 - $instance = new self();
1942 1996
1943 - // Track failures in batch
1944 - $batch_stats = [
1945 - 'processed' => 0,
1946 - 'failed' => 0,
1947 - 'last_error' => ''
1948 - ];
1997 +</div>
1949 1998
1950 - for ($i = $start_url; $i < $end_url; $i++) {
1951 - $page_url = esc_url_raw($urls[$i]);
1952 - $page_response = wp_remote_get($page_url);
1999 +<!-- Knowledge Base Table Card -->
2000 +<div class="mxchat-card">
2001 + <div class="mxchat-card-header">
2002 +<h2>
2003 + <?php esc_html_e('Knowledge Base', 'mxchat'); ?>
2004 + <span class="mxchat-record-count">
2005 + <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2006 + (<?php echo esc_html($total_records); ?> of <?php echo esc_html(number_format($total_in_database)); ?> total - recent entries only)
2007 + <?php else: ?>
2008 + (<?php echo esc_html($total_records); ?>)
2009 + <?php endif; ?>
2010 + </span>
2011 + <!-- Keep your existing badge code here -->
2012 + <?php if ($use_pinecone) : ?>
2013 + <span class="mxchat-data-source-badge pinecone" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #e3f2fd; color: #1976d2;">
2014 + <span class="dashicons dashicons-cloud"></span>
2015 + <?php esc_html_e('Pinecone', 'mxchat'); ?>
2016 + </span>
2017 + <?php else : ?>
2018 + <span class="mxchat-data-source-badge wordpress" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: 500; margin-left: 8px; background: #f3e5f5; color: #7b1fa2;">
2019 + <span class="dashicons dashicons-database-view"></span>
2020 + <?php esc_html_e('WordPress DB', 'mxchat'); ?>
2021 + </span>
2022 + <?php endif; ?>
2023 +</h2>
2024 + <div class="mxchat-header-actions">
2025 + <!-- Search -->
2026 + <form method="get" id="knowledge-search" class="mxchat-search-form">
2027 + <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
2028 + <input type="hidden" name="page" value="mxchat-prompts" />
2029 + <div class="mxchat-search-group">
2030 + <span class="dashicons dashicons-search"></span>
2031 + <input type="text"
2032 + name="search"
2033 + placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
2034 + value="<?php echo esc_attr($search_query); ?>" />
2035 + </div>
2036 + </form>
1953 2037
1954 - if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) {
1955 - $batch_stats['failed']++;
1956 - continue;
1957 - }
2038 + <!-- Delete All -->
2039 + <form method="post"
2040 + action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
2041 + class="mxchat-delete-form"
2042 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
2043 + <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
2044 + <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" />
2045 + <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
2046 + <span class="dashicons dashicons-trash"></span>
2047 + <?php esc_html_e('Delete All', 'mxchat'); ?>
2048 + </button>
2049 + </form>
2050 + </div>
2051 + </div>
1958 2052
1959 - $page_html = wp_remote_retrieve_body($page_response);
1960 - $page_content = $instance->mxchat_extract_main_content($page_html);
1961 - $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content);
2053 + <!-- Recent Entries Info Banner -->
2054 +<?php if ($showing_recent_only && $total_in_database > 1000): ?>
2055 + <div class="mxchat-info-banner recent-only" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e3f2fd; border-left: 4px solid #2196f3; color: #1565c0;">
2056 + <span class="dashicons dashicons-info"></span>
2057 + <span>
2058 + <?php printf(
2059 + esc_html__('Showing your %s most recent entries for faster loading. Your complete database contains %s entries. ', 'mxchat'),
2060 + number_format($total_records),
2061 + number_format($total_in_database)
2062 + ); ?>
2063 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('View complete database in Pinecone →', 'mxchat'); ?></a>
2064 + </span>
2065 + </div>
2066 +<?php endif; ?>
1962 2067
1963 - if (!empty($sanitized_content)) {
1964 - $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content);
2068 + <!-- Data Source Info Banner -->
2069 + <?php if ($use_pinecone) : ?>
2070 + <div class="mxchat-info-banner pinecone" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #e8f5e8; border-left: 4px solid #4caf50; color: #2e7d2e;">
2071 + <span class="dashicons dashicons-cloud"></span>
2072 + <span><?php esc_html_e('Refresh page to see new content, but wait 5-10 seconds first! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2073 + </div>
2074 + <?php else : ?>
2075 + <div class="mxchat-info-banner wordpress" style="display: flex; align-items: center; gap: 8px; padding: 12px 16px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; background: #fff3e0; border-left: 4px solid #ff9800; color: #e65100;">
2076 + <span class="dashicons dashicons-database-view"></span>
2077 + <span><?php esc_html_e('Refresh page to see new content, but wait 5-10 seconds first! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2078 + </div>
2079 + <?php endif; ?>
1965 2080
1966 - if (is_array($embedding_vector)) {
1967 - $options = get_option('mxchat_options');
1968 - $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']);
2081 + <!-- Table -->
2082 + <div class="mxchat-table-wrapper">
2083 + <table class="mxchat-records-table">
2084 + <thead>
2085 + <tr>
2086 + <th><?php esc_html_e('ID', 'mxchat'); ?></th>
2087 + <th><?php esc_html_e('Content', 'mxchat'); ?></th>
2088 + <th><?php esc_html_e('Source', 'mxchat'); ?></th>
2089 + <?php if ($data_source === 'pinecone') : ?>
2090 + <th><?php esc_html_e('Vector ID', 'mxchat'); ?></th>
2091 + <?php endif; ?>
2092 + <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2093 + </tr>
2094 + </thead>
2095 + <tbody>
2096 + <?php if ($prompts) : ?>
2097 + <?php foreach ($prompts as $index => $prompt) : ?>
2098 + <tr id="prompt-<?php echo esc_attr($prompt->id); ?>"
2099 + data-source="<?php echo esc_attr($data_source); ?>"
2100 + <?php if ($data_source === 'pinecone') : ?>
2101 + style="background: rgba(33, 150, 243, 0.02);"
2102 + <?php endif; ?>>
2103 + <td>
2104 + <?php if ($data_source === 'pinecone') : ?>
2105 + <?php echo esc_html($index + 1 + (($current_page - 1) * $per_page)); ?>
2106 + <?php else : ?>
2107 + <?php echo esc_html($prompt->id); ?>
2108 + <?php endif; ?>
2109 + </td>
2110 + <td class="mxchat-content-cell">
2111 + <div class="content-view">
2112 + <?php
2113 + $content = $prompt->article_content;
2114 + // Check if content contains Hebrew characters
2115 + if (preg_match('/[\x{0590}-\x{05FF}]/u', $content)) {
2116 + // Apply RTL direction for Hebrew content
2117 + echo '<div dir="rtl" lang="he" class="rtl-content">';
2118 + echo wp_kses_post(wpautop(esc_textarea($content)));
2119 + echo '</div>';
2120 + } else {
2121 + echo wp_kses_post(wpautop(esc_textarea($content)));
2122 + }
2123 + ?>
2124 + </div>
2125 + <?php if ($data_source === 'wordpress') : ?>
2126 + <textarea class="content-edit" style="display:none;"
2127 + <?php if (preg_match('/[\x{0590}-\x{05FF}]/u', $prompt->article_content)) echo 'dir="rtl" lang="he"'; ?>>
2128 + <?php echo htmlspecialchars($prompt->article_content, ENT_QUOTES, 'UTF-8'); ?>
2129 + </textarea>
2130 + <?php endif; ?>
2131 + </td>
2132 + <td class="mxchat-url-cell">
2133 + <div class="url-view">
2134 + <?php if (!empty($prompt->source_url)) : ?>
2135 + <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
2136 + <span class="dashicons dashicons-external"></span>
2137 + <?php esc_html_e('View Source', 'mxchat'); ?>
2138 + </a>
2139 + <?php else : ?>
2140 + <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
2141 + <?php endif; ?>
2142 + </div>
2143 + <?php if ($data_source === 'wordpress') : ?>
2144 + <input type="text" class="url-edit" style="display:none;"
2145 + value="<?php echo htmlspecialchars($prompt->source_url, ENT_QUOTES, 'UTF-8'); ?>" />
2146 + <?php endif; ?>
2147 + </td>
2148 + <?php if ($data_source === 'pinecone') : ?>
2149 + <td class="mxchat-vector-id-cell">
2150 + <code style="background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 11px;">
2151 + <?php echo esc_html(substr($prompt->id, 0, 12) . '...'); ?>
2152 + </code>
2153 + </td>
2154 + <?php endif; ?>
2155 +
2156 +<td class="mxchat-actions-cell">
2157 + <?php if ($data_source === 'wordpress') : ?>
2158 + <!-- WordPress - Full editing capabilities -->
2159 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2160 + <select class="mxchat-role-select"
2161 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2162 + data-data-source="wordpress"
2163 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2164 + <?php
2165 + $current_role = $prompt->role_restriction ?? 'public';
2166 + $role_options = $knowledge_manager->mxchat_get_role_options();
2167 + foreach ($role_options as $role_key => $role_label) : ?>
2168 + <option value="<?php echo esc_attr($role_key); ?>"
2169 + <?php selected($current_role, $role_key); ?>>
2170 + <?php echo esc_html($role_label); ?>
2171 + </option>
2172 + <?php endforeach; ?>
2173 + </select>
2174 + </div>
2175 +
2176 + <!-- Edit/Save Buttons -->
2177 + <div class="mxchat-action-buttons">
2178 + <button class="mxchat-button-icon edit-button"
2179 + data-id="<?php echo esc_attr($prompt->id); ?>">
2180 + <span class="dashicons dashicons-edit"></span>
2181 + </button>
2182 + <button class="mxchat-button-icon save-button"
2183 + data-id="<?php echo esc_attr($prompt->id); ?>"
2184 + style="display:none;"
2185 + data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
2186 + <span class="dashicons dashicons-saved"></span>
2187 + </button>
2188 + </div>
2189 + <?php else : ?>
2190 + <!-- Pinecone - Role restriction editable, content read-only -->
2191 + <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2192 + <select class="mxchat-role-select"
2193 + data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2194 + data-data-source="pinecone"
2195 + data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2196 + <?php
2197 + $current_role = $prompt->role_restriction ?? 'public';
2198 + $role_options = $knowledge_manager->mxchat_get_role_options();
2199 + foreach ($role_options as $role_key => $role_label) : ?>
2200 + <option value="<?php echo esc_attr($role_key); ?>"
2201 + <?php selected($current_role, $role_key); ?>>
2202 + <?php echo esc_html($role_label); ?>
2203 + </option>
2204 + <?php endforeach; ?>
2205 + </select>
2206 + </div>
2207 +
2208 + <?php endif; ?>
1969 2209
1970 - if (is_wp_error($submission_result)) {
1971 - $batch_stats['failed']++;
1972 - $batch_stats['last_error'] = $submission_result->get_error_message();
1973 - continue;
1974 - }
2210 + <!-- Delete button for both sources -->
2211 + <div class="mxchat-delete-container">
2212 + <?php if ($data_source === 'pinecone') : ?>
2213 + <!-- AJAX Delete for Pinecone -->
2214 + <button type="button"
2215 + class="mxchat-button-icon delete-button-ajax"
2216 + data-vector-id="<?php echo esc_attr($prompt->id); ?>"
2217 + data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
2218 + title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
2219 + <span class="dashicons dashicons-trash"></span>
2220 + </button>
2221 + <?php else : ?>
2222 + <!-- Regular link delete for WordPress DB -->
2223 + <a href="<?php echo esc_url(admin_url(
2224 + 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
2225 + . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
2226 + )); ?>"
2227 + class="mxchat-button-icon delete-button"
2228 + onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
2229 + <span class="dashicons dashicons-trash"></span>
2230 + </a>
2231 + <?php endif; ?>
2232 + </div>
2233 +</td>
1975 2234
1976 - $batch_stats['processed']++;
1977 - } else {
1978 - $batch_stats['failed']++;
1979 - $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.';
1980 - }
1981 - }
1982 2235
1983 - $status['processed_urls'] = absint($i + 1);
1984 - $status['last_update'] = time();
1985 - $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed'];
1986 - $status['last_error'] = $batch_stats['last_error'];
2236 + </tr>
2237 + <?php endforeach; ?>
2238 + <?php else : ?>
2239 + <tr>
2240 + <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
2241 + <?php if ($use_pinecone) : ?>
2242 + <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
2243 + <?php else : ?>
2244 + <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
2245 + <?php endif; ?>
2246 + </td>
2247 + </tr>
2248 + <?php endif; ?>
2249 + </tbody>
2250 + </table>
2251 + </div>
1987 2252
1988 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
1989 - }
2253 + <?php if ($page_links) : ?>
2254 + <div class="mxchat-pagination">
2255 + <?php echo wp_kses_post($page_links); ?>
2256 + </div>
2257 + <?php endif; ?>
2258 +</div>
1990 2259
1991 - // If all URLs in this batch failed, stop processing
1992 - if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) {
1993 - $status['status'] = 'error';
1994 - $status['error'] = sprintf(
1995 - 'Processing stopped: %d consecutive failures. Last error: %s',
1996 - $batch_stats['failed'],
1997 - $batch_stats['last_error']
1998 - );
1999 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2000 - return;
2001 - }
2260 +</div>
2002 2261
2003 - if ($end_url < $total_urls) {
2004 - wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array(
2005 - 'urls' => array_map('esc_url_raw', $urls),
2006 - 'sitemap_url' => $sitemap_url,
2007 - 'total_urls' => $total_urls,
2008 - 'batch_size' => $batch_size,
2009 - 'batch_pause' => $batch_pause,
2010 - ));
2011 - } else {
2012 - $status['status'] = 'complete';
2013 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2014 - }
2015 - } catch (\Exception $e) {
2016 - $status['status'] = 'error';
2017 - $status['error'] = sanitize_text_field($e->getMessage());
2018 - set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS);
2019 - }
2020 -}
2262 + <!-- Sync Settings Tab (Initially Hidden) -->
2263 + <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
2264 +<div class="mxchat-card">
2265 + <!-- Auto-Sync Settings -->
2266 + <div class="mxchat-settings-section">
2267 + <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
2268 + <p class="mxchat-description">
2269 + <?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'); ?>
2270 + </p>
2271 + <div class="mxchat-autosave-section">
2272 + <div class="mxchat-toggle-group">
2273 + <div class="mxchat-toggle-container">
2274 + <label class="mxchat-toggle-switch">
2275 + <input type="checkbox"
2276 + name="mxchat_auto_sync_posts"
2277 + class="mxchat-autosave-field"
2278 + value="1"
2279 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2280 + <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
2281 + <span class="mxchat-toggle-slider"></span>
2282 + </label>
2283 + <span class="mxchat-toggle-label">
2284 + <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
2285 + </span>
2286 + </div>
2021 2287
2022 -public function get_sitemap_processing_status($sitemap_url) {
2023 - $sitemap_url = esc_url_raw($sitemap_url);
2024 - $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)));
2288 + <div class="mxchat-toggle-container">
2289 + <label class="mxchat-toggle-switch">
2290 + <input type="checkbox"
2291 + name="mxchat_auto_sync_pages"
2292 + class="mxchat-autosave-field"
2293 + value="1"
2294 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2295 + <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
2296 + <span class="mxchat-toggle-slider"></span>
2297 + </label>
2298 + <span class="mxchat-toggle-label">
2299 + <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
2300 + </span>
2301 + </div>
2025 2302
2026 - if (!$status || !is_array($status)) {
2027 - return false;
2028 - }
2303 + <!-- Custom Post Types Section -->
2304 + <div class="mxchat-section-content">
2305 + <div class="mxchat-custom-post-types-header">
2306 + <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
2307 + <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
2308 + <span class="mxchat-toggle-icon">▼</span>
2309 + </button>
2310 + </div>
2029 2311
2030 - return array(
2031 - 'total_urls' => absint($status['total_urls']),
2032 - 'processed_urls' => absint($status['processed_urls']),
2033 - 'failed_urls' => absint($status['failed_urls'] ?? 0),
2034 - 'percentage' => ($status['total_urls'] > 0)
2035 - ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100)
2036 - : 0,
2037 - 'status' => sanitize_text_field($status['status']),
2038 - 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'),
2039 - 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '',
2040 - 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : ''
2041 - );
2042 -}
2312 + <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
2313 + <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
2314 + <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
2043 2315
2044 -public function mxchat_stop_processing() {
2045 - // Verify permissions
2046 - if (!current_user_can('manage_options')) {
2047 - wp_die(esc_html__('Unauthorized access', 'mxchat'));
2048 - }
2316 + <div class="mxchat-custom-post-types">
2317 + <?php
2318 + $post_types = $knowledge_manager->mxchat_get_public_post_types();
2319 + // Skip post and page as they're handled separately
2320 + unset($post_types['post']);
2321 + unset($post_types['page']);
2049 2322
2050 - // Verify nonce
2051 - check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce');
2323 + if (!empty($post_types)) {
2324 + foreach ($post_types as $post_type => $label) {
2325 + $option_name = 'mxchat_auto_sync_' . $post_type;
2326 + $is_enabled = get_option($option_name, '0');
2327 + ?>
2328 + <div class="mxchat-toggle-container">
2329 + <label class="mxchat-toggle-switch">
2330 + <input type="checkbox"
2331 + name="<?php echo esc_attr($option_name); ?>"
2332 + class="mxchat-autosave-field"
2333 + value="1"
2334 + data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2335 + <?php checked($is_enabled, '1'); ?>>
2336 + <span class="mxchat-toggle-slider"></span>
2337 + </label>
2338 + <span class="mxchat-toggle-label">
2339 + <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
2340 + </span>
2341 + </div>
2342 + <?php
2343 + }
2344 + } else {
2345 + echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
2346 + }
2347 + ?>
2348 + </div>
2349 + </div>
2350 + </div>
2351 + </div>
2352 + </div>
2353 + </div>
2354 +</div>
2355 +</div>
2052 2356
2053 - // Get the last sitemap URL and clear its transient
2054 - $sitemap_url = get_transient('mxchat_last_sitemap_url');
2055 - if ($sitemap_url) {
2056 - delete_transient('mxchat_sitemap_status_' . md5($sitemap_url));
2057 - delete_transient('mxchat_last_sitemap_url');
2058 - }
2357 +<!-- NEW PINECONE TAB -->
2358 + <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
2359 + <div class="mxchat-card">
2360 + <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
2361 + <p class="mxchat-description">
2362 + <?php echo wp_kses(
2363 + __('<strong>Pinecone is optional</strong> and not required for MxChat to function. It provides enhanced search performance for larger knowledge bases. When enabled, content will be stored in Pinecone instead of the WordPress database, but you can use MxChat without it.', 'mxchat'),
2364 + array('strong' => array())
2365 + ); ?>
2366 + </p>
2059 2367
2060 - // Get the last PDF URL and clear its transient
2061 - $pdf_url = get_transient('mxchat_last_pdf_url');
2062 - if ($pdf_url) {
2063 - delete_transient('mxchat_pdf_status_' . md5($pdf_url));
2064 - delete_transient('mxchat_last_pdf_url');
2065 - }
2368 + <div class="mxchat-pinecone-info-box">
2369 + <div class="mxchat-info-icon">
2370 + <span class="dashicons dashicons-info"></span>
2371 + </div>
2372 + <div class="mxchat-info-content">
2373 + <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
2374 + <p><?php esc_html_e('Pinecone is a specialized vector database designed for AI applications. It provides faster similarity searches and better scalability compared to traditional databases for AI-powered features.', 'mxchat'); ?></p>
2375 + <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
2376 + </div>
2377 + </div>
2066 2378
2067 - // Unschedule any pending sitemap events
2068 - $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls');
2069 - if ($timestamp) {
2070 - wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls');
2071 - }
2379 + <div class="mxchat-database-settings-form">
2380 + <!-- REMOVED the WordPress form - we're using AJAX auto-save instead -->
2381 + <?php
2382 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
2383 + $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
2384 + ?>
2072 2385
2073 - // Redirect back with a success message
2074 - set_transient('mxchat_admin_notice_success',
2075 - esc_html__('Processing has been stopped successfully.', 'mxchat'),
2076 - 30
2077 - );
2078 - wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts'));
2079 - exit;
2080 -}
2081 -private function mxchat_sanitize_content_for_api($content) {
2082 - // Remove script, style tags, and HTML comments
2083 - $content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $content);
2084 - $content = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $content);
2085 - $content = preg_replace('/<!--(.|\s)*?-->/', '', $content);
2386 + <div class="mxchat-toggle-container">
2387 + <label class="mxchat-toggle-switch">
2388 + <input type="checkbox"
2389 + name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
2390 + value="1"
2391 + <?php checked($use_pinecone, '1'); ?>>
2392 + <span class="mxchat-toggle-slider"></span>
2393 + </label>
2394 + <span class="mxchat-toggle-label">
2395 + <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
2396 + </span>
2397 + </div>
2086 2398
2087 - // Remove all HTML tags and decode HTML entities
2088 - $content = wp_strip_all_tags($content);
2089 - $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5);
2399 + <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
2090 2400
2091 - // Trim and normalize whitespace
2092 - $content = trim(preg_replace('/\s+/', ' ', $content));
2401 + <?php if ($use_pinecone) : ?>
2402 + <div class="mxchat-knowledge-warning success">
2403 + <p><span class="dashicons dashicons-yes-alt"></span>
2404 + <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
2405 + </p>
2406 + </div>
2407 + <?php endif; ?>
2093 2408
2094 - return $content;
2095 -}
2409 + <div class="mxchat-form-group">
2410 + <label for="mxchat_pinecone_api_key">
2411 + <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
2412 + </label>
2413 + <input type="password"
2414 + id="mxchat_pinecone_api_key"
2415 + name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
2416 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
2417 + class="regular-text"
2418 + placeholder="pcsk_..." />
2419 + <p class="description">
2420 + <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
2421 + <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
2422 + </p>
2423 + </div>
2096 2424
2425 + <div class="mxchat-form-group">
2426 + <label for="mxchat_pinecone_environment">
2427 + <?php esc_html_e('Region', 'mxchat'); ?>
2428 + </label>
2429 + <input type="text"
2430 + id="mxchat_pinecone_environment"
2431 + name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
2432 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
2433 + placeholder="e.g., gcp-starter"
2434 + class="regular-text" />
2435 + <p class="description">
2436 + <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
2437 + </p>
2438 + </div>
2097 2439
2440 + <div class="mxchat-form-group">
2441 + <label for="mxchat_pinecone_index">
2442 + <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
2443 + </label>
2444 + <input type="text"
2445 + id="mxchat_pinecone_index"
2446 + name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
2447 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
2448 + placeholder="e.g., my-wordpress-vectors"
2449 + class="regular-text" />
2450 + <p class="description">
2451 + <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
2452 + </p>
2453 + </div>
2098 2454
2455 + <div class="mxchat-form-group">
2456 + <label for="mxchat_pinecone_host">
2457 + <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
2458 + </label>
2459 + <input type="text"
2460 + id="mxchat_pinecone_host"
2461 + name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
2462 + value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
2463 + placeholder="e.g., my-index-xyz123.svc.pinecone.io"
2464 + class="regular-text" />
2465 + <p class="description">
2466 + <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
2467 + </p>
2468 + </div>
2099 2469
2100 -public function mxchat_handle_product_change($post_id, $post, $update) {
2101 - if ($post->post_type !== 'product') {
2102 - return;
2103 - }
2470 +<div class="mxchat-setup-steps">
2471 + <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
2472 + <div class="mxchat-setup-step">
2473 + <span class="step-number">1</span>
2474 + <div class="step-content">
2475 + <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
2476 + <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
2477 + </div>
2478 + </div>
2479 + <div class="mxchat-setup-step">
2480 + <span class="step-number">2</span>
2481 + <div class="step-content">
2482 + <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
2483 + <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
2484 + <ul>
2485 + <li><?php esc_html_e('Dimensions: Choose based on your embedding model - 1536 (OpenAI Ada 2, TE3 Small), 2048 (Voyage-3 Large), 3072 (TE3 Large, Gemini Embedding), 1536 (Gemini Embedding), or 768 (Gemini Embedding)', 'mxchat'); ?></li>
2486 + <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
2487 + <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
2488 + </ul>
2489 + </div>
2490 + </div>
2491 + <div class="mxchat-setup-step">
2492 + <span class="step-number">3</span>
2493 + <div class="step-content">
2494 + <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
2495 + <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
2496 + </div>
2497 + </div>
2498 + <div class="mxchat-setup-step">
2499 + <span class="step-number">4</span>
2500 + <div class="step-content">
2501 + <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
2502 + <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
2503 + </div>
2504 + </div>
2505 + <div class="mxchat-setup-step">
2506 + <span class="step-number">5</span>
2507 + <div class="step-content">
2508 + <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
2509 + <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
2510 + </div>
2511 + </div>
2512 +</div>
2104 2513
2105 - if ($post->post_status === 'publish') {
2106 - add_action('shutdown', function() use ($post_id) {
2107 - $product = wc_get_product($post_id);
2108 - if ($product) {
2109 - $this->mxchat_store_product_embedding($product);
2110 - }
2111 - });
2112 - }
2113 -}
2114 2514
2115 -private function mxchat_store_product_embedding($product) {
2116 - if (!isset($this->options['enable_woocommerce_integration']) ||
2117 - !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) {
2118 - return;
2119 - }
2120 2515
2121 - $source_url = get_permalink($product->get_id());
2516 + </div>
2122 2517
2123 - // Build description
2124 - $regular_price = $product->get_regular_price();
2125 - $sale_price = $product->get_sale_price();
2126 - $description = $product->get_description() . "\n\n" .
2127 - esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" .
2128 - esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" .
2129 - esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" .
2130 - esc_html__('SKU:', 'mxchat') . " " . $product->get_sku();
2131 2518
2132 - // Generate embedding vector
2133 - $embedding_vector = $this->mxchat_generate_embedding($description);
2134 - if (!is_array($embedding_vector)) {
2135 - return;
2136 - }
2519 + </div>
2520 + </div>
2521 + </div>
2137 2522
2138 - // Check for Pinecone addon and its settings
2139 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2140 - $use_pinecone = false;
2523 +</div>
2141 2524
2142 - if ($pinecone_settings && is_array($pinecone_settings)) {
2143 - $use_pinecone = (
2144 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2145 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2146 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2147 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2148 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2149 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2150 - );
2151 - }
2152 2525
2153 - if ($use_pinecone) {
2154 - $result = $this->store_in_pinecone_main(
2155 - $embedding_vector,
2156 - $description,
2157 - $source_url,
2158 - $pinecone_settings['mxchat_pinecone_api_key'],
2159 - $pinecone_settings['mxchat_pinecone_environment'],
2160 - $pinecone_settings['mxchat_pinecone_index']
2161 - );
2162 2526
2163 - if (!$result['success']) {
2164 - // Fallback to WordPress DB if Pinecone storage fails
2165 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2166 - }
2167 - } else {
2168 - // Use WordPress DB storage
2169 - $this->store_in_wordpress_db($description, $source_url, $embedding_vector);
2170 - }
2171 -}
2172 2527
2173 -public function mxchat_handle_product_delete($post_id) {
2174 - if (get_post_type($post_id) !== 'product') {
2175 - return;
2176 - }
2528 +</div>
2529 +</div>
2177 2530
2178 - $source_url = get_permalink($post_id);
2179 2531
2180 - // Check for Pinecone addon and its settings
2181 - $pinecone_settings = get_option('mxchat_pinecone_addon_options');
2182 - $use_pinecone = false;
2532 +<!-- Content Selector Modal -->
2533 +<div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
2534 + <div class="mxchat-kb-modal-content">
2535 +<div class="mxchat-kb-modal-header">
2536 + <h3>
2537 + <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
2538 + <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
2539 + </h3>
2540 + <span class="mxchat-kb-modal-close">&times;</span>
2541 +</div>
2542 + <div class="mxchat-kb-modal-filters">
2543 + <div class="mxchat-kb-search-group">
2544 + <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
2545 + </div>
2183 2546
2184 - if ($pinecone_settings && is_array($pinecone_settings)) {
2185 - $use_pinecone = (
2186 - isset($pinecone_settings['mxchat_use_pinecone']) &&
2187 - $pinecone_settings['mxchat_use_pinecone'] === '1' &&
2188 - !empty($pinecone_settings['mxchat_pinecone_api_key']) &&
2189 - !empty($pinecone_settings['mxchat_pinecone_host']) &&
2190 - !empty($pinecone_settings['mxchat_pinecone_index']) &&
2191 - !empty($pinecone_settings['mxchat_pinecone_environment'])
2192 - );
2193 - }
2547 + <div class="mxchat-kb-filter-group">
2548 + <select id="mxchat-kb-content-type-filter">
2549 + <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2550 + <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
2551 + <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
2552 + <?php
2553 + // Add other post types dynamically
2554 + $post_types = get_post_types(array('public' => true), 'objects');
2555 + foreach ($post_types as $post_type) {
2556 + // Skip post and page as they're already added
2557 + if (!in_array($post_type->name, array('post', 'page'))) {
2558 + echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
2559 + }
2560 + }
2561 + ?>
2562 + </select>
2194 2563
2195 - if ($use_pinecone) {
2196 - $this->delete_from_pinecone(
2197 - array($source_url),
2198 - $pinecone_settings['mxchat_pinecone_api_key'],
2199 - $pinecone_settings['mxchat_pinecone_environment'],
2200 - $pinecone_settings['mxchat_pinecone_index']
2201 - );
2202 - } else {
2203 - // Fallback to WordPress DB deletion
2204 - global $wpdb;
2205 - $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
2206 - $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
2207 - }
2208 -}
2564 + <select id="mxchat-kb-content-status-filter">
2565 + <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
2566 + <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
2567 + <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
2568 + </select>
2209 2569
2210 -/**
2211 - * Delete vectors from Pinecone by source URL
2212 - *
2213 - * @param array $urls Array of source URLs to delete
2214 - * @param string $api_key Pinecone API key
2215 - * @param string $environment Pinecone environment
2216 - * @param string $index_name Pinecone index name
2217 - * @return array Associative array with 'success' boolean and 'message' string
2218 - */
2219 -private function delete_from_pinecone($urls, $api_key, $environment, $index_name) {
2220 - // Get the Pinecone host from options (matching your store_in_pinecone_main pattern)
2221 - $options = get_option('mxchat_pinecone_addon_options');
2222 - $host = $options['mxchat_pinecone_host'] ?? '';
2570 + <select id="mxchat-kb-processed-filter">
2571 + <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
2572 + <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
2573 + <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
2574 + </select>
2575 + </div>
2576 + </div>
2223 2577
2224 - if (empty($host)) {
2225 - return array(
2226 - 'success' => false,
2227 - 'message' => 'Pinecone host is not configured. Please set the host in your settings.'
2228 - );
2229 - }
2578 + <div class="mxchat-kb-content-selection">
2579 + <div class="mxchat-kb-selection-header">
2580 + <label>
2581 + <input type="checkbox" id="mxchat-kb-select-all">
2582 + <?php esc_html_e('Select All', 'mxchat'); ?>
2583 + </label>
2584 + <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
2585 + </div>
2230 2586
2231 - // Build API endpoint using the configured host
2232 - $api_endpoint = "https://{$host}/vectors/delete";
2587 + <div class="mxchat-kb-content-list">
2588 + <!-- Content will be loaded here via AJAX -->
2589 + <div class="mxchat-kb-loading">
2590 + <span class="mxchat-kb-spinner is-active"></span>
2591 + <?php esc_html_e('Loading content...', 'mxchat'); ?>
2592 + </div>
2593 + </div>
2233 2594
2234 - // Create vector IDs from URLs (matching your store method's ID generation)
2235 - $vector_ids = array_map('md5', $urls);
2595 + <div class="mxchat-kb-pagination">
2596 + <!-- Pagination will be added here -->
2597 + </div>
2598 + </div>
2236 2599
2237 - // Prepare the delete request body
2238 - $request_body = array(
2239 - 'ids' => $vector_ids,
2240 - 'filter' => array(
2241 - 'source_url' => array(
2242 - '$in' => $urls
2243 - )
2244 - )
2245 - );
2600 + <div class="mxchat-kb-modal-footer">
2601 + <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
2602 + <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
2603 + <span class="mxchat-kb-selected-count">(0)</span>
2604 + </button>
2605 + <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
2606 + <?php esc_html_e('Cancel', 'mxchat'); ?>
2607 + </button>
2608 + </div>
2609 + </div>
2610 +</div>
2246 2611
2247 - // Make the deletion request
2248 - $response = wp_remote_post($api_endpoint, array(
2249 - 'headers' => array(
2250 - 'Api-Key' => $api_key,
2251 - 'accept' => 'application/json',
2252 - 'content-type' => 'application/json'
2253 - ),
2254 - 'body' => wp_json_encode($request_body),
2255 - 'timeout' => 30,
2256 - 'data_format' => 'body'
2257 - ));
2612 +<?php
2613 +}
2258 2614
2259 - // Handle WordPress HTTP API errors
2260 - if (is_wp_error($response)) {
2261 - return array(
2262 - 'success' => false,
2263 - 'message' => $response->get_error_message()
2264 - );
2265 - }
2266 2615
2267 - // Check response status
2268 - $response_code = wp_remote_retrieve_response_code($response);
2269 - if ($response_code !== 200) {
2270 - $body = wp_remote_retrieve_body($response);
2271 - return array(
2272 - 'success' => false,
2273 - 'message' => sprintf(
2274 - 'Pinecone API error (HTTP %d): %s',
2275 - $response_code,
2276 - $body
2277 - )
2278 - );
2279 - }
2616 +
2617 +
2280 2618
2281 - // Parse response body
2282 - $body = wp_remote_retrieve_body($response);
2283 - $response_data = json_decode($body, true);
2284 -
2285 - // Final validation of the response
2286 - if (json_last_error() !== JSON_ERROR_NONE) {
2287 - return array(
2288 - 'success' => false,
2289 - 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg()
2290 - );
2619 +public function mxchat_delete_chat_history() {
2620 + if (!current_user_can('manage_options')) {
2621 + echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
2622 + wp_die();
2291 2623 }
2292 -
2293 - return array(
2294 - 'success' => true,
2295 - 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids))
2296 - );
2297 -}
2298 -
2299 -
2300 -public function mxchat_fetch_chat_history() {
2624 + check_ajax_referer('mxchat_delete_chat_history', 'security');
2301 2625 global $wpdb;
2302 2626 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2303 2627
2304 - if (!current_user_can('manage_options')) {
2305 - wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
2306 - }
2628 + if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
2629 + $deleted_count = 0;
2307 2630
2308 - // First get unique session IDs ordered by most recent message in each session
2309 - $session_ids = $wpdb->get_col(
2310 - $wpdb->prepare(
2311 - "SELECT DISTINCT session_id
2312 - FROM {$table_name}
2313 - GROUP BY session_id
2314 - ORDER BY MAX(timestamp) DESC"
2315 - )
2316 - );
2631 + foreach ($_POST['delete_session_ids'] as $session_id) {
2632 + $session_id_sanitized = sanitize_text_field($session_id);
2317 2633
2318 - if (empty($session_ids)) {
2319 - wp_die(esc_html__('No chat history available.', 'mxchat'));
2320 - }
2634 + // Clear relevant cache before deletion
2635 + $cache_key = 'chat_session_' . $session_id_sanitized;
2636 + wp_cache_delete($cache_key, 'mxchat_chat_sessions');
2321 2637
2322 - ob_start();
2323 - echo '<div class="mxchat-transcript">';
2638 + // Perform the deletion from the database table
2639 + $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
2324 2640
2325 - // Iterate through sessions from newest to oldest
2326 - foreach ($session_ids as $session_id) {
2327 - // Get the email associated with this session (if available)
2328 - $email = $wpdb->get_var(
2329 - $wpdb->prepare(
2330 - "SELECT user_email
2331 - FROM {$table_name}
2332 - WHERE session_id = %s AND user_email != ''
2333 - ORDER BY timestamp ASC
2334 - LIMIT 1",
2335 - $session_id
2336 - )
2337 - );
2641 + // Delete the corresponding option entry from wp_options table
2642 + delete_option("mxchat_history_" . $session_id_sanitized);
2338 2643
2339 - // Debug: Check email retrieval
2340 - if (empty($email)) {
2341 - //error_log("No email found for session {$session_id}");
2342 - } else {
2343 - //error_log("Email for session {$session_id}: " . $email);
2644 + // Delete any associated metadata options
2645 + delete_option("mxchat_email_" . $session_id_sanitized);
2646 + delete_option("mxchat_agent_name_" . $session_id_sanitized);
2647 +
2648 + $deleted_count++;
2344 2649 }
2345 2650
2346 - // Get messages for this session ordered by timestamp
2347 - $messages = $wpdb->get_results(
2348 - $wpdb->prepare(
2349 - "SELECT * FROM {$table_name}
2350 - WHERE session_id = %s
2351 - ORDER BY timestamp ASC",
2352 - $session_id
2651 + // Optionally, clear a general cache if you have one
2652 + wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
2653 +
2654 + echo wp_json_encode([
2655 + 'success' => sprintf(
2656 + esc_html__('%d chat session(s) have been deleted from all storage locations.', 'mxchat'),
2657 + $deleted_count
2353 2658 )
2354 - );
2659 + ]);
2660 + } else {
2661 + echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
2662 + }
2355 2663
2356 - // Start session block
2357 - echo '<div class="mxchat-session">';
2358 - echo '<div class="mxchat-session-header">';
2359 - // Wrap checkbox and session ID in one block
2360 - echo '<div class="mxchat-session-id">';
2361 - echo '<input type="checkbox" name="delete_session_ids[]" value="' . esc_attr($session_id) . '"> ';
2362 - echo '<strong>' . esc_html__('Session ID:', 'mxchat') . '</strong> ' . esc_html($session_id);
2363 - echo '</div>';
2664 + wp_die();
2665 +}
2666 +public function display_admin_notices() {
2667 + // Check if we're on a MXChat admin page
2668 + $screen = get_current_screen();
2669 + if (!$screen || strpos($screen->base, 'mxchat') === false) {
2670 + return;
2671 + }
2364 2672
2365 - // Place email directly below the session ID block
2366 - if (!empty($email)) {
2367 - echo '<div class="mxchat-session-email">';
2368 - echo '<strong>' . esc_html__('Email:', 'mxchat') . '</strong> ' . esc_html($email);
2369 - echo '</div>';
2370 - }
2371 - echo '</div>';
2673 + //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
2372 2674
2373 - echo '<div class="mxchat-messages">';
2675 + // Check for error notices
2676 + $error_notice = get_transient('mxchat_admin_notice_error');
2677 + if ($error_notice) {
2678 + //error_log('Found error transient: ' . $error_notice);
2679 + echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
2680 + delete_transient('mxchat_admin_notice_error');
2681 + //error_log('Displayed and deleted error transient');
2682 + } else {
2683 + //error_log('No error transient found');
2684 + }
2374 2685
2375 - // Display messages for this session
2376 - foreach ($messages as $transcript) {
2377 - $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp));
2686 + // Check for success notices
2687 + $success_notice = get_transient('mxchat_admin_notice_success');
2688 + if ($success_notice) {
2689 + //error_log('Found success transient: ' . $success_notice);
2690 + echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
2691 + delete_transient('mxchat_admin_notice_success');
2692 + //error_log('Displayed and deleted success transient');
2693 + }
2378 2694
2379 - // Determine message styling
2380 - switch ($transcript->role) {
2381 - case 'assistant':
2382 - case 'bot':
2383 - $message_class = 'bot-message';
2384 - $display_role = esc_html__('Chatbot', 'mxchat');
2385 - break;
2386 - case 'user':
2387 - $message_class = 'user-message';
2388 - $display_role = !empty($transcript->user_identifier)
2389 - ? sanitize_text_field($transcript->user_identifier)
2390 - : esc_html__('User', 'mxchat');
2391 - break;
2392 - case 'agent':
2393 - $message_class = 'agent-message';
2394 - $display_role = esc_html__('Agent', 'mxchat');
2395 - break;
2396 - default:
2397 - $message_class = 'unknown-message';
2398 - $display_role = esc_html__('Unknown', 'mxchat');
2399 - }
2400 -
2401 - // Process message content
2402 - $message_content = wp_kses(
2403 - stripslashes($transcript->message),
2404 - [
2405 - 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
2406 - 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
2407 - 'a' => ['href' => [], 'title' => []]
2408 - ]
2409 - );
2410 - $message_content = nl2br($message_content);
2411 -
2412 - // Render message
2413 - echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
2414 - echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
2415 - echo '<div class="mxchat-message-content">' . $message_content . '</div>';
2416 - echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
2417 - echo '</div>';
2418 - }
2419 -
2420 - // Close session block
2421 - echo '</div></div>';
2695 + // Check for info notices
2696 + $info_notice = get_transient('mxchat_admin_notice_info');
2697 + if ($info_notice) {
2698 + //error_log('Found info transient: ' . $info_notice);
2699 + echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
2700 + delete_transient('mxchat_admin_notice_info');
2701 + //error_log('Displayed and deleted info transient');
2422 2702 }
2423 2703
2424 - echo '</div>';
2425 - $output = ob_get_clean();
2426 - echo $output;
2427 - wp_die();
2428 2704 }
2429 2705
2430 2706
2431 2707 public function mxchat_create_activation_page() {
@@ -2430,16 +2706,26 @@
2430 2706
2431 2707 public function mxchat_create_activation_page() {
2432 2708 $license_status = get_option('mxchat_license_status', 'inactive');
2433 2709 $license_error = get_option('mxchat_license_error', '');
2710 + $current_domain = parse_url(home_url(), PHP_URL_HOST);
2711 +
2712 + $license_management_url = 'https://mxchat.ai/my-account/orders/';
2713 +
2714 + // Check if this activation has been linked to a domain
2715 + $is_domain_linked = $this->is_current_activation_linked($current_domain);
2434 2716 ?>
2435 2717 <div class="wrap mxchat-admin-activation">
2436 2718 <div class="mxchat-pro-hero">
2437 2719 <h1 class="pro-title">
2438 - <span class="pro-gradient-text">Activate</span> MxChat Pro
2720 + <span class="pro-gradient-text">Manage</span> MxChat Pro
2439 2721 </h1>
2440 2722 <p class="pro-subtitle">
2441 - <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2723 + <?php if ($license_status === 'active'): ?>
2724 + <?php esc_html_e('Your pro license is active. Manage your activation below.', 'mxchat'); ?>
2725 + <?php else: ?>
2726 + <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2727 + <?php endif; ?>
2442 2728 </p>
2443 2729 </div>
2444 2730
2445 2731 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
@@ -2447,8 +2733,25 @@
2447 2733 <p><?php echo esc_html($license_error); ?></p>
2448 2734 </div>
2449 2735 <?php endif; ?>
2450 2736
2737 + <!-- Domain Linking Notice for Existing Users -->
2738 + <?php if ($license_status === 'active' && !$is_domain_linked): ?>
2739 + <div class="notice notice-info">
2740 + <p>
2741 + <strong><?php esc_html_e('New Feature: Domain Tracking', 'mxchat'); ?></strong><br>
2742 + <?php esc_html_e('We\'ve enhanced our license system to track your activated domains. Link this activation to your domain for better license management.', 'mxchat'); ?>
2743 + </p>
2744 + <p>
2745 + <button type="button" id="link-domain-button" class="button button-secondary">
2746 + <?php esc_html_e('Link This Domain', 'mxchat'); ?>
2747 + </button>
2748 + <span id="domain-link-status" style="margin-left: 10px;"></span>
2749 + </p>
2750 + </div>
2751 + <?php endif; ?>
2752 +
2753 + <!-- Activation Form -->
2451 2754 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2452 2755 <div class="mxchat-pro-form-container">
2453 2756 <table class="form-table">
2454 2757 <tr valign="top">
@@ -2463,8 +2766,12 @@
2463 2766 <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 />
2464 2767 </td>
2465 2768 </tr>
2466 2769 </table>
2770 +
2771 + <!-- Hidden field for domain -->
2772 + <input type="hidden" id="mxchat_domain" name="domain" value="<?php echo esc_attr($current_domain); ?>" />
2773 +
2467 2774 <?php if ($license_status !== 'active'): ?>
2468 2775 <div class="mxchat-pro-button-container">
2469 2776 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2470 2777 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
@@ -2471,8 +2778,9 @@
2471 2778 </div>
2472 2779 <?php endif; ?>
2473 2780 </div>
2474 2781 </form>
2782 +
2475 2783 <!-- License Status Display -->
2476 2784 <div class="mxchat-pro-status">
2477 2785 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2478 2786 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
@@ -2478,15 +2786,91 @@
2478 2786 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2479 2787 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2480 2788 </span>
2481 2789 </h3>
2790 +
2791 + <?php if ($license_status === 'active'): ?>
2792 + <div class="mxchat-license-details">
2793 + <p><strong><?php esc_html_e('Domain:', 'mxchat'); ?></strong> <?php echo esc_html($current_domain); ?>
2794 + <?php if ($is_domain_linked): ?>
2795 + <span style="color: green; margin-left: 10px;">✓ <?php esc_html_e('Linked', 'mxchat'); ?></span>
2796 + <?php else: ?>
2797 + <span style="color: orange; margin-left: 10px;">⚠ <?php esc_html_e('Not Linked', 'mxchat'); ?></span>
2798 + <?php endif; ?>
2799 + </p>
2800 + <p><strong><?php esc_html_e('Email:', 'mxchat'); ?></strong> <?php echo esc_html(get_option('mxchat_pro_email')); ?></p>
2801 + <p><strong><?php esc_html_e('License Key:', 'mxchat'); ?></strong>
2802 + <code style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px;">
2803 + <?php echo esc_html(get_option('mxchat_activation_key')); ?>
2804 + </code>
2805 + </p>
2806 + <p>
2807 + <small>
2808 + <?php esc_html_e('Manage all your licenses and domains on', 'mxchat'); ?>
2809 + <a href="<?php echo esc_url($license_management_url); ?>" target="_blank">
2810 + <?php esc_html_e('your account dashboard', 'mxchat'); ?>
2811 + </a>
2812 + </small>
2813 + </p>
2814 +
2815 + <!-- Deactivation Section -->
2816 + <div class="mxchat-deactivation-section" style="margin-top: 20px; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
2817 + <h4 style="margin-top: 0; color: #856404;">
2818 + <?php esc_html_e('License Management', 'mxchat'); ?>
2819 + </h4>
2820 + <p style="margin-bottom: 15px; color: #856404;">
2821 + <?php esc_html_e('Need to move this license to another site? Deactivate it here first, then activate it on your new site.', 'mxchat'); ?>
2822 + </p>
2823 + <button type="button" id="deactivate-license-button" class="button button-secondary" style="background: #dc3545; color: white; border-color: #dc3545;">
2824 + 🔓 <?php esc_html_e('Deactivate License', 'mxchat'); ?>
2825 + </button>
2826 + <span id="deactivate-status" style="margin-left: 10px;"></span>
2827 + </div>
2828 + </div>
2829 + <?php endif; ?>
2482 2830 </div>
2483 2831
2832 + <!-- Hidden fields for JavaScript -->
2833 + <input type="hidden" id="mxchat-ajax-url" value="<?php echo admin_url('admin-ajax.php'); ?>" />
2834 + <input type="hidden" id="mxchat-nonce" value="<?php echo wp_create_nonce('mxchat_activate_license_nonce'); ?>" />
2835 + <input type="hidden" id="mxchat-api-url" value="https://mxchat.ai" />
2484 2836 </div>
2485 2837 <?php
2486 2838 }
2487 2839
2488 -public function mxchat_intents_page_html() {
2840 +/**
2841 + * Check if current activation is linked to a domain
2842 + * This checks YOUR website's database, not the user's local database
2843 + */
2844 +private function is_current_activation_linked($domain) {
2845 + $license_key = get_option('mxchat_activation_key');
2846 + $email = get_option('mxchat_pro_email');
2847 +
2848 + if (empty($license_key) || empty($email)) {
2849 + return false;
2850 + }
2851 +
2852 + // Check with YOUR website's API
2853 + $response = wp_remote_post('https://mxchat.ai/mxchat-api/check-domain', array(
2854 + 'body' => array(
2855 + 'license_key' => $license_key,
2856 + 'email' => $email,
2857 + 'domain' => $domain
2858 + ),
2859 + 'timeout' => 10,
2860 + 'sslverify' => false
2861 + ));
2862 +
2863 + if (is_wp_error($response)) {
2864 + return false;
2865 + }
2866 +
2867 + $body = json_decode(wp_remote_retrieve_body($response), true);
2868 + return isset($body['success']) && $body['success'] && isset($body['data']['linked']) && $body['data']['linked'];
2869 +}
2870 +
2871 +
2872 +public function mxchat_actions_page_html() {
2489 2873 if (!current_user_can('manage_options')) {
2490 2874 return;
2491 2875 }
2492 2876
@@ -2499,9 +2883,9 @@
2499 2883
2500 2884 // Success message
2501 2885 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2502 2886 echo '<div class="notice notice-success is-dismissible"><p>' .
2503 - esc_html__('Intent updated successfully.', 'mxchat') .
2887 + esc_html__('Action updated successfully.', 'mxchat') .
2504 2888 '</p></div>';
2505 2889 }
2506 2890
2507 2891 // Filtering logic
@@ -2522,10 +2906,10 @@
2522 2906 // Pagination
2523 2907 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2524 2908 $total_pages = ceil($total_intents / $per_page);
2525 2909
2526 - // Get intents
2527 - $intents = $wpdb->get_results($wpdb->prepare(
2910 + // Get intents (now called actions)
2911 + $actions = $wpdb->get_results($wpdb->prepare(
2528 2912 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2529 2913 $per_page, $offset
2530 2914 ));
2531 2915
@@ -2536,605 +2920,813 @@
2536 2920 <div class="wrap mxchat-wrapper">
2537 2921 <!-- Hero Section -->
2538 2922 <div class="mxchat-hero">
2539 2923 <h1 class="mxchat-main-title">
2540 - <span class="mxchat-gradient-text">Intent</span> Manager
2924 + <span class="mxchat-gradient-text">Actions</span> Manager
2541 2925 </h1>
2542 2926 <p class="mxchat-hero-subtitle">
2543 - <?php esc_html_e('Create and manage custom intents to enhance your chatbots understanding and response capabilities.', 'mxchat'); ?>
2927 + <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
2544 2928 </p>
2545 2929 </div>
2546 2930
2547 - <div class="mxchat-content">
2548 - <!-- Add Intent Card -->
2549 - <div class="mxchat-card">
2550 - <div class="mxchat-card-header">
2551 - <h2><?php esc_html_e('Add New Intent', 'mxchat'); ?></h2>
2552 - </div>
2931 + <!-- Actions Header with Search and Filter -->
2932 + <div class="mxchat-actions-header">
2933 + <div class="mxchat-actions-filters">
2934 + <form method="get" class="mxchat-search-form">
2935 + <input type="hidden" name="page" value="mxchat-actions">
2936 + <div class="mxchat-search-group">
2937 + <span class="dashicons dashicons-search"></span>
2938 + <input type="text" name="s" class="mxchat-search-input"
2939 + placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
2940 + value="<?php echo esc_attr($search_term); ?>">
2941 + </div>
2942 + <select name="callback_filter" class="mxchat-action-filter">
2943 + <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
2944 + <?php foreach ($available_callbacks as $function => $callback_data) :
2945 + $label = $callback_data['label']; ?>
2946 + <option value="<?php echo esc_attr($function); ?>"
2947 + <?php selected($callback_filter, $function); ?>>
2948 + <?php echo esc_html($label); ?>
2949 + </option>
2950 + <?php endforeach; ?>
2951 + </select>
2952 + <button type="submit" class="mxchat-button-secondary">
2953 + <?php esc_html_e('Filter', 'mxchat'); ?>
2954 + </button>
2955 + </form>
2956 + </div>
2957 + <div class="mxchat-actions-controls">
2958 + <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
2959 + <span class="dashicons dashicons-plus-alt"></span>
2960 + <?php esc_html_e('Add New Action', 'mxchat'); ?>
2961 + </button>
2962 + </div>
2963 + </div>
2553 2964
2554 - <div class="mxchat-intent-documentation">
2555 - <p>
2556 - <?php esc_html_e('We highly encourage users to quickly read our ', 'mxchat'); ?>
2557 - <a href="https://mxchat.ai/documentation/#intents" target="_blank" rel="noopener noreferrer">
2558 - <?php esc_html_e('documentation', 'mxchat'); ?>
2559 - </a>
2560 - <?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'); ?>
2561 - </p>
2562 - </div>
2965 + <!-- Actions Grid Layout - All actions in a single grid -->
2966 + <div class="mxchat-actions-grid">
2967 + <div class="mxchat-cards-container">
2968 + <?php if (!empty($actions)) : ?>
2969 + <?php foreach ($actions as $action) :
2970 + $callback_function = $action->callback_function;
2971 + $callback_label = isset($available_callbacks[$callback_function]['label'])
2972 + ? $available_callbacks[$callback_function]['label']
2973 + : $callback_function;
2974 + $threshold_value = isset($action->similarity_threshold)
2975 + ? round($action->similarity_threshold * 100)
2976 + : 85;
2563 2977
2564 - <form id="mxchat-add-intent-form" method="post"
2565 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2566 - <input type="hidden" name="action" value="mxchat_add_intent">
2567 - <?php wp_nonce_field('mxchat_add_intent_nonce'); ?>
2978 + // Check if this is a form action
2979 + $is_form_action = strpos($action->intent_label, 'Form ') === 0;
2568 2980
2569 - <div class="mxchat-form-group">
2570 - <label for="intent_label">
2571 - <?php esc_html_e('Intent Label (For your reference only)', 'mxchat'); ?>
2572 - </label>
2573 - <input name="intent_label" type="text" id="intent_label" required
2574 - class="mxchat-intent-input"
2575 - placeholder="<?php esc_attr_e('Example Email Capture: Newsletter Signup', 'mxchat'); ?>">
2576 - </div>
2981 + // Get action status (enabled/disabled) - default to true if column doesn't exist
2982 + $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
2983 + ?>
2984 + <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
2985 + <div class="mxchat-card-header">
2986 + <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
2987 + <div class="mxchat-card-toggle">
2988 + <label class="mxchat-switch">
2989 + <input type="checkbox" class="mxchat-action-toggle"
2990 + data-action-id="<?php echo esc_attr($action->id); ?>"
2991 + <?php checked($is_enabled); ?>>
2992 + <span class="mxchat-slider round"></span>
2993 + </label>
2994 + </div>
2995 + </div>
2577 2996
2578 - <div class="mxchat-form-group">
2579 - <label for="phrases">
2580 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2581 - </label>
2582 - <textarea name="phrases" id="phrases" rows="5" required
2583 - class="mxchat-intent-textarea"
2584 - 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>
2585 - </div>
2997 + <div class="mxchat-card-body">
2998 + <div class="mxchat-card-description">
2999 + <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
3000 + <?php echo esc_html($callback_label); ?>
3001 + </div>
2586 3002
2587 - <div class="mxchat-form-group">
2588 - <label for="callback_function">
2589 - <?php esc_html_e('Callback Function', 'mxchat'); ?>
2590 - </label>
2591 - <select name="callback_function" id="callback_function"
2592 - class="mxchat-intent-select" required>
2593 - <option value="">
2594 - <?php esc_html_e('Select a Callback', 'mxchat'); ?>
2595 - </option>
2596 - <?php
2597 - $groups = $this->mxchat_get_available_callbacks(true);
2598 - foreach ($groups as $group_label => $group_callbacks) :
2599 - echo '<optgroup label="' . esc_attr($group_label) . '">';
2600 - foreach ($group_callbacks as $function => $data) :
2601 - $label = $data['label'];
2602 - $pro_only = $data['pro_only'];
2603 - $disabled = (!$this->is_activated && $pro_only) ? 'disabled' : '';
2604 - $label_suffix = (!$this->is_activated && $pro_only) ? ' (Pro Only)' : '';
2605 - ?>
2606 - <option value="<?php echo esc_attr($function); ?>"
2607 - <?php echo $disabled; ?>>
2608 - <?php echo esc_html($label . $label_suffix); ?>
2609 - </option>
3003 + <div class="mxchat-card-phrases">
3004 + <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
3005 + <div class="mxchat-phrases-preview">
3006 + <?php
3007 + // Check if the helper function exists, otherwise use a simple substring
3008 + if (method_exists($this, 'get_trimmed_phrases')) {
3009 + echo esc_html($this->get_trimmed_phrases($action->phrases));
3010 + } else {
3011 + echo esc_html(strlen($action->phrases) > 100 ?
3012 + substr($action->phrases, 0, 97) . '...' :
3013 + $action->phrases);
3014 + }
3015 + ?>
3016 + </div>
3017 + </div>
3018 +
3019 + <div class="mxchat-threshold-control">
3020 + <div class="mxchat-threshold-label">
3021 + <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
3022 + <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
3023 + </div>
3024 + </div>
3025 + </div>
3026 +
3027 + <div class="mxchat-card-footer">
2610 3028 <?php
2611 - endforeach;
2612 - echo '</optgroup>';
2613 - endforeach;
2614 - ?>
2615 - </select>
2616 - </div>
3029 + // Check if it's a form action
3030 + $is_form_action = preg_match('/Form (\d+)/', $action->intent_label, $form_matches);
2617 3031
2618 - <button type="submit" class="mxchat-button-primary">
2619 - <?php esc_html_e('Add Intent', 'mxchat'); ?>
2620 - </button>
2621 - </form>
2622 - </div>
3032 + // Check if it's a recommendation flow action
3033 + $is_flow_action = preg_match('/Recommendation Flow (\d+)/', $action->intent_label, $flow_matches);
2623 3034
2624 - <!-- Manage Intents Card -->
2625 - <div class="mxchat-card">
2626 - <div class="mxchat-card-header">
2627 - <h2><?php esc_html_e('Manage Intents', 'mxchat'); ?></h2>
2628 - <div class="mxchat-header-actions">
2629 - <form method="get" class="mxchat-search-form">
2630 - <input type="hidden" name="page" value="mxchat-intents">
2631 - <div class="mxchat-search-group">
2632 - <span class="dashicons dashicons-search"></span>
2633 - <input type="text" name="s"
2634 - placeholder="<?php esc_attr_e('Search Intents', 'mxchat'); ?>"
2635 - value="<?php echo esc_attr($search_term); ?>">
2636 - <select name="callback_filter" class="mxchat-intent-filter">
2637 - <option value="">
2638 - <?php esc_html_e('All Callbacks', 'mxchat'); ?>
2639 - </option>
2640 - <?php foreach ($available_callbacks as $function => $callback_data) :
2641 - $label = $callback_data['label']; ?>
2642 - <option value="<?php echo esc_attr($function); ?>"
2643 - <?php selected($callback_filter, $function); ?>>
2644 - <?php echo esc_html($label); ?>
2645 - </option>
2646 - <?php endforeach; ?>
2647 - </select>
2648 - <button type="submit" class="mxchat-button-secondary">
2649 - <?php esc_html_e('Filter', 'mxchat'); ?>
2650 - </button>
3035 + if ($is_form_action) {
3036 + $form_id = isset($form_matches[1]) ? $form_matches[1] : '';
3037 + ?>
3038 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
3039 + class="mxchat-button-primary">
3040 + <span class="dashicons dashicons-feedback"></span>
3041 + <?php esc_html_e('Edit Form', 'mxchat'); ?>
3042 + </a>
3043 + <?php } elseif ($is_flow_action) {
3044 + ?>
3045 + <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-smart-recommender')); ?>"
3046 + class="mxchat-button-primary">
3047 + <span class="dashicons dashicons-list-view"></span>
3048 + <?php esc_html_e('Manage Flows', 'mxchat'); ?>
3049 + </a>
3050 + <?php } else { ?>
3051 + <button type="button"
3052 + class="mxchat-button-secondary mxchat-edit-button"
3053 + data-action-id="<?php echo esc_attr($action->id); ?>"
3054 + data-phrases="<?php echo esc_attr($action->phrases); ?>"
3055 + data-label="<?php echo esc_attr($action->intent_label); ?>"
3056 + data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
3057 + data-callback-function="<?php echo esc_attr($action->callback_function); ?>">
3058 + <span class="dashicons dashicons-edit"></span>
3059 + <?php esc_html_e('Edit', 'mxchat'); ?>
3060 + </button>
3061 + <form method="post"
3062 + action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
3063 + class="mxchat-delete-form"
3064 + onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
3065 + <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
3066 + <input type="hidden" name="action" value="mxchat_delete_intent">
3067 + <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
3068 + <button type="submit" class="mxchat-button-text mxchat-delete-button">
3069 + <span class="dashicons dashicons-trash"></span>
3070 + <?php esc_html_e('Delete', 'mxchat'); ?>
3071 + </button>
3072 + </form>
3073 + <?php } ?>
2651 3074 </div>
2652 - </form>
2653 - </div>
2654 - </div>
2655 3075
2656 - <div class="mxchat-table-wrapper">
2657 - <table class="mxchat-records-table">
2658 - <thead>
2659 - <tr>
2660 - <th><?php esc_html_e('Intent Label', 'mxchat'); ?></th>
2661 - <th><?php esc_html_e('Phrases', 'mxchat'); ?></th>
2662 - <th><?php esc_html_e('Callback Function', 'mxchat'); ?></th>
2663 - <th><?php esc_html_e('Similarity Threshold', 'mxchat'); ?></th>
2664 - <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2665 - </tr>
2666 - </thead>
2667 - <tbody>
2668 - <?php if ($intents) :
2669 - foreach ($intents as $intent) :
2670 - $callback_function = $intent->callback_function;
2671 - $callback_label = isset($available_callbacks[$callback_function]['label'])
2672 - ? $available_callbacks[$callback_function]['label']
2673 - : $callback_function;
2674 - $threshold_value = isset($intent->similarity_threshold)
2675 - ? round($intent->similarity_threshold * 100)
2676 - : 85;
2677 -
2678 - // Check if this is a form intent (its intent_label starts with "Form ")
2679 - $is_form_intent = strpos($intent->intent_label, 'Form ') === 0;
2680 - ?>
2681 - <tr<?php echo $is_form_intent ? ' class="mxchat-form-intent"' : ''; ?>>
2682 - <td>
2683 - <?php
2684 - if ($is_form_intent) {
2685 - // Attempt to extract the form ID from the intent_label.
2686 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2687 - $form_id = isset($matches[1]) ? intval($matches[1]) : 0;
2688 - if ($form_id) {
2689 - global $wpdb;
2690 - $forms_table = $wpdb->prefix . 'mxchat_forms';
2691 - $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id));
2692 - if ($form && !empty($form->title)) {
2693 - echo esc_html($form->title);
2694 - } else {
2695 - echo esc_html($intent->intent_label);
2696 - }
2697 - } else {
2698 - echo esc_html($intent->intent_label);
2699 - }
2700 - echo ' <span class="mxchat-badge">Form Intent</span>';
2701 - } else {
2702 - echo esc_html($intent->intent_label);
2703 - }
2704 - ?>
2705 - </td>
2706 - <td class="mxchat-content-cell">
2707 - <?php echo esc_html($intent->phrases); ?>
2708 - </td>
2709 - <td><?php echo esc_html($callback_label); ?></td>
2710 - <td>
2711 - <!-- Similarity threshold adjustment (shown for all intents) -->
2712 - <form method="post"
2713 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2714 - class="mxchat-threshold-form">
2715 - <?php wp_nonce_field('mxchat_update_intent_threshold_nonce'); ?>
2716 - <input type="hidden" name="action"
2717 - value="mxchat_update_intent_threshold">
2718 - <input type="hidden" name="intent_id"
2719 - value="<?php echo esc_attr($intent->id); ?>">
2720 - <div class="mxchat-slider-group">
2721 - <input type="range"
2722 - name="intent_threshold"
2723 - id="intent_threshold_<?php echo esc_attr($intent->id); ?>"
2724 - min="70"
2725 - max="95"
2726 - value="<?php echo esc_attr($threshold_value); ?>"
2727 - class="mxchat-intent-slider"
2728 - oninput="document.getElementById('threshold_output_<?php echo esc_attr($intent->id); ?>').value = this.value + '%'">
2729 - <output id="threshold_output_<?php echo esc_attr($intent->id); ?>"
2730 - class="mxchat-intent-output">
2731 - <?php echo esc_html($threshold_value); ?>%
2732 - </output>
2733 - </div>
2734 - <button type="submit" class="mxchat-button-icon">
2735 - <span class="dashicons dashicons-saved"></span>
2736 - </button>
2737 - </form>
2738 - </td>
2739 - <td class="mxchat-actions-cell">
2740 - <?php if (!$is_form_intent) : ?>
2741 - <!-- Standard intents: allow edit and delete -->
2742 - <button type="button"
2743 - class="mxchat-button-icon mxchat-edit-button"
2744 - data-intent-id="<?php echo esc_attr($intent->id); ?>"
2745 - data-phrases="<?php echo esc_attr($intent->phrases); ?>">
2746 - <span class="dashicons dashicons-edit"></span>
2747 - </button>
2748 - <form method="post"
2749 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2750 - class="mxchat-delete-form"
2751 - onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this intent?', 'mxchat'); ?>');">
2752 - <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2753 - <input type="hidden" name="action" value="mxchat_delete_intent">
2754 - <input type="hidden" name="intent_id"
2755 - value="<?php echo esc_attr($intent->id); ?>">
2756 - <button type="submit" class="mxchat-button-icon">
2757 - <span class="dashicons dashicons-trash"></span>
2758 - </button>
2759 - </form>
2760 - <?php else : ?>
2761 - <!-- Form intents: show manage in forms button -->
2762 - <?php
2763 - preg_match('/Form (\d+)/', $intent->intent_label, $matches);
2764 - $form_id = isset($matches[1]) ? $matches[1] : '';
2765 - ?>
2766 - <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2767 - class="mxchat-button-secondary">
2768 - <?php esc_html_e('Manage in Forms', 'mxchat'); ?>
2769 - </a>
2770 - <?php endif; ?>
2771 - </td>
2772 - </tr>
2773 - <?php endforeach;
2774 - else : ?>
2775 - <tr>
2776 - <td colspan="5" class="mxchat-no-records">
2777 - <?php esc_html_e('No intents found.', 'mxchat'); ?>
2778 - </td>
2779 - </tr>
2780 - <?php endif; ?>
2781 - </tbody>
2782 - </table>
2783 - </div>
2784 3076
2785 - <?php if ($total_pages > 1) : ?>
2786 - <div class="mxchat-pagination">
2787 - <?php
2788 - echo paginate_links(array(
2789 - 'base' => add_query_arg('paged', '%#%'),
2790 - 'format' => '',
2791 - 'prev_text' => __('&laquo; Previous', 'mxchat'),
2792 - 'next_text' => __('Next &raquo;', 'mxchat'),
2793 - 'total' => $total_pages,
2794 - 'current' => $page
2795 - ));
2796 - ?>
3077 + </div>
3078 + <?php endforeach; ?>
3079 + <?php else : ?>
3080 + <!-- If no actions found -->
3081 + <div class="mxchat-no-actions">
3082 + <div class="mxchat-empty-state">
3083 + <span class="dashicons dashicons-format-chat"></span>
3084 + <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
3085 + <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
3086 + <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
3087 + <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
3088 + </button>
3089 + </div>
2797 3090 </div>
2798 3091 <?php endif; ?>
2799 3092 </div>
2800 3093 </div>
2801 3094
2802 - <!-- Edit Modal -->
2803 -<div id="mxchat-edit-modal" class="mxchat-modal" style="display: none;">
3095 + <?php if ($total_pages > 1) : ?>
3096 + <div class="mxchat-pagination">
3097 + <?php
3098 + echo paginate_links(array(
3099 + 'base' => add_query_arg('paged', '%#%'),
3100 + 'format' => '',
3101 + 'prev_text' => __('&laquo; Previous', 'mxchat'),
3102 + 'next_text' => __('Next &raquo;', 'mxchat'),
3103 + 'total' => $total_pages,
3104 + 'current' => $page
3105 + ));
3106 + ?>
3107 + </div>
3108 + <?php endif; ?>
3109 +
3110 +<!-- Add/Edit Action Modal with Step-Based Approach -->
3111 +<!-- Complete Modal HTML with Defined Groups Variable -->
3112 +<div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
2804 3113 <div class="mxchat-modal-content">
2805 3114 <span class="mxchat-modal-close">&times;</span>
2806 - <div class="mxchat-card-header">
2807 - <h2><?php esc_html_e('Edit Intent Phrases', 'mxchat'); ?></h2>
2808 - </div>
2809 3115
2810 - <form id="mxchat-edit-intent-form" method="post"
2811 - action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2812 - <?php wp_nonce_field('mxchat_edit_intent_nonce'); ?>
2813 - <input type="hidden" name="action" value="mxchat_edit_intent">
2814 - <input type="hidden" name="intent_id" id="edit_intent_id">
3116 + <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3117 + <!-- Dynamic nonce field -->
3118 + <div id="action-nonce-container">
3119 + <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
3120 + </div>
3121 + <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
3122 + <input type="hidden" name="intent_id" id="edit_action_id" value="">
3123 + <input type="hidden" name="callback_function" id="callback_function" value="">
2815 3124
2816 - <div class="mxchat-form-group">
2817 - <label for="edit_phrases">
2818 - <?php esc_html_e('Phrases (comma-separated)', 'mxchat'); ?>
2819 - </label>
2820 - <textarea name="phrases"
2821 - id="edit_phrases"
2822 - rows="5"
2823 - class="mxchat-intent-textarea"
2824 - required></textarea>
3125 + <!-- Step 1: Action Type Selection -->
3126 + <div id="mxchat-action-step-1" class="mxchat-action-step active">
3127 + <div class="mxchat-step-indicator">
3128 + <div class="mxchat-step-number">1</div>
3129 + <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
3130 + </div>
3131 +
3132 + <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
3133 + <div class="mxchat-action-type-search">
3134 + <span class="dashicons dashicons-search"></span>
3135 + <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
3136 + </div>
3137 +
3138 + <?php
3139 + // Get the callbacks - IMPORTANT: Define the $groups variable here
3140 + $groups = $this->mxchat_get_available_callbacks(true, true);
3141 + ?>
3142 +
3143 + <div class="mxchat-action-type-categories">
3144 + <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
3145 + <?php
3146 + // Get unique categories from the defined groups
3147 + foreach ($groups as $group_label => $group_callbacks) :
3148 + $category_slug = sanitize_title($group_label);
3149 + ?>
3150 + <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
3151 + <?php endforeach; ?>
3152 + </div>
3153 +
3154 + <div class="mxchat-action-types-grid">
3155 + <?php
3156 + // Generate action cards from available callbacks
3157 + foreach ($groups as $group_label => $group_callbacks) :
3158 + $category_slug = sanitize_title($group_label);
3159 +
3160 + foreach ($group_callbacks as $function => $data) :
3161 + $label = $data['label'];
3162 + $pro_only = $data['pro_only'];
3163 + $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
3164 + $description = isset($data['description']) ? $data['description'] : '';
3165 + $is_addon = isset($data['addon']) && $data['addon'] !== false;
3166 + $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
3167 + $is_installed = isset($data['installed']) ? $data['installed'] : true;
3168 +
3169 + // Determine card status and styling
3170 + $card_class = 'mxchat-action-type-card';
3171 + $icon_class = 'mxchat-action-type-icon';
3172 + $status_badge = '';
3173 +
3174 + if ($pro_only && !$this->is_activated) {
3175 + // Pro feature but no Pro license
3176 + $icon_class .= ' pro-feature';
3177 + $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
3178 + }
3179 +
3180 + if ($is_addon && !$is_installed) {
3181 + // Add-on not installed
3182 + $card_class .= ' not-installed';
3183 + $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3184 + }
3185 +
3186 + // Default description if none provided
3187 + if (empty($description)) {
3188 + $description = sprintf(
3189 + esc_html__('Use the %s action in your chatbot', 'mxchat'),
3190 + $label
3191 + );
3192 + }
3193 + ?>
3194 + <div class="<?php echo esc_attr($card_class); ?>"
3195 + data-category="<?php echo esc_attr($category_slug); ?>"
3196 + data-value="<?php echo esc_attr($function); ?>"
3197 + data-label="<?php echo esc_attr($label); ?>"
3198 + data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
3199 + data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
3200 + data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
3201 + <div class="<?php echo esc_attr($icon_class); ?>">
3202 + <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
3203 + </div>
3204 + <div class="mxchat-action-type-info">
3205 + <h4><?php echo esc_html($label); ?></h4>
3206 + <p><?php echo esc_html($description); ?></p>
3207 + <?php if (!empty($status_badge)) : ?>
3208 + <?php echo $status_badge; ?>
3209 + <?php endif; ?>
3210 +
3211 + <?php if ($is_addon && !$is_installed) : ?>
3212 + <div class="mxchat-addon-info">
3213 + <?php echo esc_html(sprintf(
3214 + __('Requires %s', 'mxchat'),
3215 + $addon_name
3216 + )); ?>
3217 + </div>
3218 + <?php endif; ?>
3219 + </div>
3220 + </div>
3221 + <?php endforeach;
3222 + endforeach; ?>
3223 + </div>
3224 + </div>
3225 +
3226 + <div class="mxchat-modal-actions">
3227 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3228 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3229 + </button>
3230 + </div>
2825 3231 </div>
2826 3232
2827 - <div class="mxchat-modal-actions">
2828 - <button type="submit" class="mxchat-button-primary">
2829 - <?php esc_html_e('Update Phrases', 'mxchat'); ?>
2830 - </button>
2831 - <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2832 - <?php esc_html_e('Cancel', 'mxchat'); ?>
2833 - </button>
3233 + <!-- Step 2: Action Configuration -->
3234 + <div id="mxchat-action-step-2" class="mxchat-action-step">
3235 + <div class="mxchat-step-indicator">
3236 + <div class="mxchat-step-number">2</div>
3237 + <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
3238 + </div>
3239 +
3240 + <div class="mxchat-selected-action">
3241 + <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
3242 + <span class="dashicons dashicons-arrow-left-alt"></span>
3243 + <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
3244 + </button>
3245 + <div class="mxchat-selected-action-info">
3246 + <div id="selected-action-icon" class="mxchat-action-type-icon">
3247 + <span class="dashicons dashicons-admin-generic"></span>
3248 + </div>
3249 + <div class="mxchat-selected-action-details">
3250 + <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
3251 + <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
3252 + </div>
3253 + </div>
3254 + </div>
3255 +
3256 + <div class="mxchat-form-group">
3257 + <label for="intent_label">
3258 + <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
3259 + </label>
3260 + <input name="intent_label" type="text" id="intent_label" required
3261 + class="mxchat-intent-input"
3262 + placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
3263 + </div>
3264 +
3265 + <div class="mxchat-form-group">
3266 + <label for="phrases">
3267 + <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
3268 + </label>
3269 + <textarea name="phrases" id="action_phrases" rows="5" required
3270 + class="mxchat-intent-textarea"
3271 + placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
3272 + </div>
3273 +
3274 + <div class="mxchat-form-group">
3275 + <label for="similarity_threshold">
3276 + <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
3277 + <span class="mxchat-threshold-value-display">85%</span>
3278 + </label>
3279 + <div class="mxchat-slider-group modal-slider">
3280 + <input type="range"
3281 + name="similarity_threshold"
3282 + id="similarity_threshold"
3283 + min="10"
3284 + max="95"
3285 + value="85"
3286 + class="mxchat-intent-slider"
3287 + oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
3288 + </div>
3289 + <div class="mxchat-threshold-hint">
3290 + <?php esc_html_e('Lower values (10-30) make the action trigger more easily. Higher values (70-95) require more exact matches.', 'mxchat'); ?>
3291 + </div>
3292 + </div>
3293 +
3294 + <div class="mxchat-modal-actions">
3295 + <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
3296 + <?php esc_html_e('Cancel', 'mxchat'); ?>
3297 + </button>
3298 + <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
3299 + <?php esc_html_e('Save Action', 'mxchat'); ?>
3300 + </button>
3301 + </div>
2834 3302 </div>
2835 3303 </form>
2836 3304 </div>
2837 3305 </div>
2838 3306
2839 - </div><!-- .mxchat-content -->
3307 +<div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
3308 + <div class="mxchat-action-loading-spinner"></div>
3309 + <div class="mxchat-action-loading-text">
3310 + <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
3311 + </div>
3312 +</div>
2840 3313 </div><!-- .mxchat-wrapper -->
3314 + <?php
3315 +}
3316 +private function get_trimmed_phrases($phrases, $max_length = 100) {
3317 + if (strlen($phrases) <= $max_length) {
3318 + return $phrases;
3319 + }
2841 3320
2842 - <div id="mxchat-intent-loading" class="mxchat-intent-loading" style="display: none;">
2843 - <div class="mxchat-intent-loading-spinner"></div>
2844 - <div class="mxchat-intent-loading-text">
2845 - <?php esc_html_e('Saving intent, please wait...', 'mxchat'); ?>
2846 - </div>
2847 - </div>
2848 -<?php
3321 + $trimmed = substr($phrases, 0, $max_length);
3322 + $last_comma = strrpos($trimmed, ',');
3323 +
3324 + if ($last_comma !== false) {
3325 + $trimmed = substr($trimmed, 0, $last_comma);
3326 + }
3327 +
3328 + return $trimmed . '...';
2849 3329 }
2850 3330
3331 +public function mxchat_add_enabled_column_to_intents() {
3332 + global $wpdb;
3333 + $table_name = $wpdb->prefix . 'mxchat_intents';
2851 3334
3335 + // Check if the column already exists
3336 + $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
2852 3337
3338 + if (empty($columns)) {
3339 + // Add the column with default value of 1 (enabled)
3340 + $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
3341 + }
3342 +}
3343 +
2853 3344 /**
2854 - * Handle editing of intent phrases
3345 + * Handle embedding generation errors using existing admin notice system
2855 3346 *
2856 - * @since 1.0.0
3347 + * @param string $message Error message to display
3348 + * @param bool $redirect Whether to redirect back to the actions page
2857 3349 * @return void
2858 3350 */
2859 -public function handle_edit_intent() {
2860 - // Verify nonce and user capabilities
2861 - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'mxchat_edit_intent_nonce')) {
2862 - wp_die(esc_html__('Security check failed.', 'mxchat'));
2863 - }
3351 +private function handle_embedding_error($message, $redirect = true) {
3352 + // Store the error message in the existing transient
3353 + set_transient('mxchat_admin_notice_error', $message, 60);
2864 3354
2865 - if (!current_user_can('manage_options')) {
2866 - wp_die(esc_html__('You do not have permission to perform this action.', 'mxchat'));
3355 + if ($redirect) {
3356 + // Redirect back to the actions page
3357 + $redirect_url = add_query_arg(
3358 + array(
3359 + 'page' => 'mxchat-actions'
3360 + ),
3361 + admin_url('admin.php')
3362 + );
3363 + wp_safe_redirect($redirect_url);
3364 + exit;
2867 3365 }
3366 +}
2868 3367
2869 - // Validate and sanitize input
2870 - $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
2871 - if (!$intent_id) {
2872 - wp_die(esc_html__('Invalid intent ID.', 'mxchat'));
2873 - }
2874 3368
2875 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2876 - if (empty($phrases_input)) {
2877 - wp_die(esc_html__('Phrases cannot be empty.', 'mxchat'));
2878 - }
3369 +/**
3370 + * Handle adding new intent - with improved error handling
3371 + *
3372 + * @return void
3373 + */
2879 3374
2880 - // Process phrases the same way as in intent creation
2881 - $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
2882 3375
2883 - if (empty($phrases_array)) {
2884 - wp_die(esc_html__('Please enter at least one valid phrase.', 'mxchat'));
2885 - }
2886 3376
2887 - // Generate embeddings and combine them
2888 - $vectors = [];
2889 - foreach ($phrases_array as $phrase) {
2890 - $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
2891 - if (is_array($embedding_vector)) {
2892 - $vectors[] = $embedding_vector;
2893 - } else {
2894 - wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase));
2895 - }
2896 - }
2897 3377
2898 - if (empty($vectors)) {
2899 - wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
3378 +/**
3379 + * Enhanced get_available_callbacks function with form action exclusion
3380 + *
3381 + * @param bool $grouped Whether to return callbacks grouped by category
3382 + * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
3383 + * @return array Callbacks data with icons, descriptions and availability status
3384 + */
3385 +private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
3386 + // Load WordPress plugin functions if needed
3387 + if (!function_exists('get_plugins')) {
3388 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
2900 3389 }
2901 3390
2902 - // Create combined vector just like in intent creation
2903 - $combined_vector = $this->mxchat_average_vectors($vectors);
2904 - $serialized_vector = maybe_serialize($combined_vector);
3391 + // Get active plugins
3392 + $active_plugins = get_option('active_plugins', array());
2905 3393
2906 - global $wpdb;
2907 - $table_name = $wpdb->prefix . 'mxchat_intents';
3394 + // Functions to exclude from the action selector only if Pro is activated
3395 + // If user doesn't have Pro, show these so they can see what they're missing
3396 + $excluded_when_pro_active_functions = array(
3397 + 'mxchat_handle_form_collection', // Forms add-on action
3398 + 'mxchat_sr_recommendation_flow' // Smart Recommender flow actions
3399 + );
2908 3400
2909 - // Update the intent with both new phrases and the combined vector
2910 - $result = $wpdb->update(
2911 - $table_name,
2912 - array(
2913 - 'phrases' => implode(', ', $phrases_array),
2914 - 'embedding_vector' => $serialized_vector
2915 - ),
2916 - array('id' => $intent_id),
2917 - array('%s', '%s'),
2918 - array('%d')
2919 - );
3401 + // Always excluded functions (regardless of Pro status)
3402 + $always_excluded_functions = array();
2920 3403
2921 - if (false === $result) {
2922 - wp_die(esc_html__('Failed to update intent.', 'mxchat'));
3404 + // Combine exclusion lists based on Pro activation status
3405 + $excluded_functions = $always_excluded_functions;
3406 + if ($this->is_activated) {
3407 + // Only exclude add-on managed functions if Pro is active
3408 + $excluded_functions = array_merge($excluded_functions, $excluded_when_pro_active_functions);
2923 3409 }
2924 3410
2925 - // Redirect back to the intents page with a success message
2926 - $redirect_url = add_query_arg(
2927 - array(
2928 - 'page' => 'mxchat-intents',
2929 - 'updated' => 'true'
3411 + // Define add-on plugin files and their corresponding action functions
3412 + $addon_plugins = array(
3413 + 'mxchat-woo/mxchat-woo.php' => array(
3414 + 'functions' => array(
3415 + 'mxchat_handle_product_recommendations',
3416 + 'mxchat_handle_order_history',
3417 + 'mxchat_show_product_card',
3418 + 'mxchat_add_to_cart',
3419 + 'mxchat_checkout_redirect'
3420 + ),
3421 + 'name' => __('WooCommerce Add-on', 'mxchat'),
3422 + 'pro_required' => true
2930 3423 ),
2931 - admin_url('admin.php')
3424 + 'mxchat-perplexity/mxchat-perplexity.php' => array(
3425 + 'functions' => array('mxchat_perplexity_research'),
3426 + 'name' => __('Perplexity Add-on', 'mxchat'),
3427 + 'pro_required' => true
3428 + ),
3429 + 'mxchat-forms/mxchat-forms.php' => array(
3430 + 'functions' => array('mxchat_handle_form_collection'),
3431 + 'name' => __('Forms Add-on', 'mxchat'),
3432 + 'pro_required' => true
3433 + ),
3434 + 'mxchat-smart-recommender/mxchat-smart-recommender.php' => array(
3435 + 'functions' => array('mxchat_sr_recommendation_flow'),
3436 + 'name' => __('Smart Recommender Add-on', 'mxchat'),
3437 + 'pro_required' => true
3438 + ),
3439 + // Add other add-ons and their functions here
2932 3440 );
2933 3441
2934 - wp_safe_redirect($redirect_url);
2935 - exit;
2936 -}
2937 -public function mxchat_handle_update_intent_threshold() {
2938 - if ( ! current_user_can( 'manage_options' ) ) {
2939 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
2940 - }
3442 + // Get the functions that are provided by active add-ons
3443 + $addon_provided_functions = array();
3444 + $addon_function_mapping = array(); // Maps functions to their add-on info
2941 3445
2942 - check_admin_referer('mxchat_update_intent_threshold_nonce');
3446 + // Check which add-ons are active
3447 + foreach ($addon_plugins as $plugin_file => $addon_info) {
3448 + $is_active = in_array($plugin_file, $active_plugins);
2943 3449
2944 - if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
2945 - global $wpdb;
2946 - $table_name = $wpdb->prefix . 'mxchat_intents';
2947 - $intent_id = intval($_POST['intent_id']);
2948 - $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
2949 - $similarity_threshold = $threshold_percentage / 100;
3450 + // For each function in this addon
3451 + foreach ($addon_info['functions'] as $function) {
3452 + // Consider a function installed only if:
3453 + // 1. The add-on is active AND
3454 + // 2. Either it doesn't require Pro OR Pro is activated
3455 + $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
2950 3456
2951 - $wpdb->update(
2952 - $table_name,
2953 - ['similarity_threshold' => $similarity_threshold],
2954 - ['id' => $intent_id],
2955 - ['%f'],
2956 - ['%d']
2957 - );
3457 + // If the add-on is installed, mark this function as provided by an add-on
3458 + if ($is_installed) {
3459 + $addon_provided_functions[] = $function;
3460 + }
3461 +
3462 + // Store addon info for this function regardless of installation status
3463 + $addon_function_mapping[$function] = array(
3464 + 'addon' => basename(dirname($plugin_file)),
3465 + 'addon_name' => $addon_info['name'],
3466 + 'pro_required' => $addon_info['pro_required'],
3467 + 'is_active' => $is_active,
3468 + 'is_installed' => $is_installed
3469 + );
3470 + }
2958 3471 }
2959 3472
2960 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
2961 - exit;
2962 -}
3473 + // Core callbacks - always available in the base plugin
3474 + $core_callbacks = array(
3475 + 'mxchat_handle_email_capture' => array(
3476 + 'label' => __('Loops Email Capture', 'mxchat'),
3477 + 'pro_only' => false,
3478 + 'group' => __('Customer Engagement', 'mxchat'),
3479 + 'icon' => 'email-alt',
3480 + 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
3481 + 'addon' => false, // Not from an add-on
3482 + 'installed' => true // Always installed with base plugin
3483 + ),
3484 + 'mxchat_handle_search_request' => array(
3485 + 'label' => __('Brave Web Search', 'mxchat'),
3486 + 'pro_only' => false,
3487 + 'group' => __('Search Features', 'mxchat'),
3488 + 'icon' => 'search',
3489 + 'description' => __('Let users search the web directly from the chat', 'mxchat'),
3490 + 'addon' => false,
3491 + 'installed' => true
3492 + ),
3493 + 'mxchat_handle_image_search_request' => array(
3494 + 'label' => __('Brave Image Search', 'mxchat'),
3495 + 'pro_only' => false,
3496 + 'group' => __('Search Features', 'mxchat'),
3497 + 'icon' => 'format-image',
3498 + 'description' => __('Search and display images in the chat conversation', 'mxchat'),
3499 + 'addon' => false,
3500 + 'installed' => true
3501 + ),
3502 + // Pro core features - check is_activated property
3503 + 'mxchat_generate_image' => array(
3504 + 'label' => __('Generate Image', 'mxchat'),
3505 + 'pro_only' => false,
3506 + 'group' => __('Other Features', 'mxchat'),
3507 + 'icon' => 'art',
3508 + 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
3509 + 'addon' => false,
3510 + 'installed' => true
3511 + ),
3512 + 'mxchat_handle_pdf_discussion' => array(
3513 + 'label' => __('Chat with PDF', 'mxchat'),
3514 + 'pro_only' => false,
3515 + 'group' => __('Other Features', 'mxchat'),
3516 + 'icon' => 'media-document',
3517 + 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
3518 + 'addon' => false,
3519 + 'installed' => true
3520 + ),
3521 + 'mxchat_live_agent_handover' => array(
3522 + 'label' => __('Slack Live Agent', 'mxchat'),
3523 + 'pro_only' => false,
3524 + 'group' => __('Customer Engagement', 'mxchat'),
3525 + 'icon' => 'admin-users',
3526 + 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
3527 + 'addon' => false,
3528 + 'installed' => true
3529 + ),
3530 + 'mxchat_handle_switch_to_chatbot_intent' => array(
3531 + 'label' => __('Back to Chatbot', 'mxchat'),
3532 + 'pro_only' => false,
3533 + 'group' => __('Customer Engagement', 'mxchat'),
3534 + 'icon' => 'backup',
3535 + 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
3536 + 'addon' => false,
3537 + 'installed' => true
3538 + ),
3539 + );
2963 3540
2964 -public function mxchat_handle_add_intent() {
2965 - if ( ! current_user_can( 'manage_options' ) ) {
2966 - wp_die( esc_html__('Unauthorized user', 'mxchat') );
2967 - }
3541 + // Add-on callbacks with placeholders - only include if the add-on is NOT active
3542 + $addon_callbacks = array(
3543 + // WooCommerce Add-on
3544 + 'mxchat_handle_product_recommendations' => array(
3545 + 'label' => __('Product Recommendations', 'mxchat'),
3546 + 'pro_only' => true,
3547 + 'group' => __('WooCommerce Features', 'mxchat'),
3548 + 'icon' => 'cart',
3549 + 'description' => __('Suggest products based on customer preferences', 'mxchat'),
3550 + ),
3551 + 'mxchat_handle_order_history' => array(
3552 + 'label' => __('Order History', 'mxchat'),
3553 + 'pro_only' => true,
3554 + 'group' => __('WooCommerce Features', 'mxchat'),
3555 + 'icon' => 'clipboard',
3556 + 'description' => __('Allow customers to check their order status', 'mxchat'),
3557 + ),
3558 + 'mxchat_show_product_card' => array(
3559 + 'label' => __('Show Product Card', 'mxchat'),
3560 + 'pro_only' => true,
3561 + 'group' => __('WooCommerce Features', 'mxchat'),
3562 + 'icon' => 'products',
3563 + 'description' => __('Display product information in the chat', 'mxchat'),
3564 + ),
3565 + 'mxchat_add_to_cart' => array(
3566 + 'label' => __('Add to Cart', 'mxchat'),
3567 + 'pro_only' => true,
3568 + 'group' => __('WooCommerce Features', 'mxchat'),
3569 + 'icon' => 'plus-alt',
3570 + 'description' => __('Add products to cart directly from chat', 'mxchat'),
3571 + ),
3572 + 'mxchat_checkout_redirect' => array(
3573 + 'label' => __('Proceed to Checkout', 'mxchat'),
3574 + 'pro_only' => true,
3575 + 'group' => __('WooCommerce Features', 'mxchat'),
3576 + 'icon' => 'arrow-right-alt',
3577 + 'description' => __('Redirect customer to checkout page', 'mxchat'),
3578 + ),
2968 3579
2969 - check_admin_referer('mxchat_add_intent_nonce');
3580 + // Perplexity Add-on
3581 + 'mxchat_perplexity_research' => array(
3582 + 'label' => __('Perplexity Research', 'mxchat'),
3583 + 'pro_only' => true,
3584 + 'group' => __('Search Features', 'mxchat'),
3585 + 'icon' => 'book-alt',
3586 + 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
3587 + ),
2970 3588
2971 - global $wpdb;
2972 - $table_name = $wpdb->prefix . 'mxchat_intents';
3589 + // Forms Add-on (only shown when Pro is not activated)
3590 + 'mxchat_handle_form_collection' => array(
3591 + 'label' => __('Form Collection', 'mxchat'),
3592 + 'pro_only' => true,
3593 + 'group' => __('Form Features', 'mxchat'),
3594 + 'icon' => 'feedback',
3595 + 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
3596 + ),
2973 3597
2974 - $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
2975 - $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
2976 - $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
2977 - $default_threshold = 0.85;
3598 + // Smart Recommender Add-on (only shown when Pro is not activated)
3599 + 'mxchat_sr_recommendation_flow' => array(
3600 + 'label' => __('Smart Recommender Flow', 'mxchat'),
3601 + 'pro_only' => true,
3602 + 'group' => __('Recommendation Features', 'mxchat'),
3603 + 'icon' => 'cart',
3604 + 'description' => __('Create interactive conversation flows that collect user preferences and deliver personalized product or service recommendations', 'mxchat'),
3605 + ),
3606 + );
2978 3607
2979 - if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
2980 - wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') );
2981 - }
3608 + // Enhance add-on callbacks with installation status and addon info
3609 + foreach ($addon_callbacks as $function => $data) {
3610 + if (isset($addon_function_mapping[$function])) {
3611 + $addon_info = $addon_function_mapping[$function];
2982 3612
2983 - $available_callbacks = $this->mxchat_get_available_callbacks();
3613 + $addon_callbacks[$function]['addon'] = $addon_info['addon'];
3614 + $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
3615 + $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
2984 3616
2985 - if (!array_key_exists($callback_function, $available_callbacks)) {
2986 - wp_die( esc_html__('Invalid callback function selected.', 'mxchat') );
3617 + // Set pro_only based on add-on configuration
3618 + $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
3619 + } else {
3620 + $addon_callbacks[$function]['addon'] = 'unknown';
3621 + $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
3622 + $addon_callbacks[$function]['installed'] = false;
3623 + }
2987 3624 }
2988 3625
2989 - $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
2990 - if ($is_pro_only && !$this->is_activated) {
2991 - wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') );
2992 - }
3626 + // Initialize callbacks with core features
3627 + $callbacks = $core_callbacks;
2993 3628
2994 - $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
3629 + // Get callbacks from active add-ons
3630 + $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
2995 3631
2996 - if (empty($phrases_array)) {
2997 - wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') );
2998 - }
3632 + // Add placeholder callbacks only for add-ons that aren't active
3633 + if ($include_all) {
3634 + foreach ($addon_callbacks as $function => $data) {
3635 + // Skip placeholders for functions provided by active add-ons
3636 + if (in_array($function, $addon_provided_functions)) {
3637 + continue;
3638 + }
2999 3639
3000 - $vectors = [];
3001 - foreach ($phrases_array as $phrase) {
3002 - $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
3003 - if (is_array($embedding_vector)) {
3004 - $vectors[] = $embedding_vector;
3005 - } else {
3006 - wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) );
3640 + // Skip excluded functions
3641 + if (in_array($function, $excluded_functions)) {
3642 + continue;
3643 + }
3644 +
3645 + // Add the placeholder
3646 + $callbacks[$function] = $data;
3007 3647 }
3008 3648 }
3009 3649
3010 - if (!empty($vectors)) {
3011 - $combined_vector = $this->mxchat_average_vectors($vectors);
3012 - $serialized_vector = maybe_serialize($combined_vector);
3650 + // Add callbacks from active add-ons (will override placeholders)
3651 + foreach ($active_addon_callbacks as $function => $data) {
3652 + // Skip excluded functions
3653 + if (in_array($function, $excluded_functions)) {
3654 + continue;
3655 + }
3013 3656
3014 - $result = $wpdb->insert($table_name, [
3015 - 'intent_label' => $intent_label,
3016 - 'phrases' => implode(', ', $phrases_array),
3017 - 'embedding_vector' => $serialized_vector,
3018 - 'callback_function' => $callback_function,
3019 - 'similarity_threshold' => $default_threshold,
3020 - ]);
3657 + // Always include callbacks from add-ons
3658 + $callbacks[$function] = $data;
3021 3659
3022 - if ($result === false) {
3023 - wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) );
3660 + // Ensure they have the proper add-on info
3661 + if (isset($addon_function_mapping[$function])) {
3662 + $addon_info = $addon_function_mapping[$function];
3663 + $callbacks[$function]['addon'] = $addon_info['addon'];
3664 + $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
3665 + $callbacks[$function]['installed'] = $addon_info['is_installed'];
3666 + $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
3024 3667 }
3025 - } else {
3026 - wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') );
3027 3668 }
3028 3669
3029 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3030 - exit;
3031 -}
3670 + // Just before returning callbacks, sort them to prioritize free features
3671 + if (!$grouped) {
3672 + // Create temporary arrays for sorting
3673 + $free_callbacks = array();
3674 + $pro_callbacks = array();
3032 3675
3676 + // Split callbacks into free and pro
3677 + foreach ($callbacks as $key => $data) {
3678 + if (isset($data['pro_only']) && $data['pro_only']) {
3679 + $pro_callbacks[$key] = $data;
3680 + } else {
3681 + $free_callbacks[$key] = $data;
3682 + }
3683 + }
3033 3684
3685 + // Merge with free callbacks first
3686 + $callbacks = array_merge($free_callbacks, $pro_callbacks);
3687 + }
3034 3688
3035 -
3036 -
3037 -
3038 -private function mxchat_get_available_callbacks($grouped = false) {
3039 - $callbacks = [
3040 - 'mxchat_handle_email_capture' => [
3041 - 'label' => __('Email Capture', 'mxchat'),
3042 - 'pro_only' => false,
3043 - 'group' => __('Customer Engagement', 'mxchat'),
3044 - ],
3045 - 'mxchat_handle_product_inquiry' => [
3046 - 'label' => __('Show Product Card', 'mxchat'),
3047 - 'pro_only' => true,
3048 - 'group' => __('WooCommerce Features', 'mxchat'),
3049 - ],
3050 - 'mxchat_handle_order_history' => [
3051 - 'label' => __('Order History', 'mxchat'),
3052 - 'pro_only' => true,
3053 - 'group' => __('WooCommerce Features', 'mxchat'),
3054 - ],
3055 - 'mxchat_generate_image' => [
3056 - 'label' => __('Generate Image', 'mxchat'),
3057 - 'pro_only' => true,
3058 - 'group' => __('Other Features', 'mxchat'),
3059 - ],
3060 - 'mxchat_handle_search_request' => [
3061 - 'label' => __('Brave Web Search', 'mxchat'),
3062 - 'pro_only' => false,
3063 - 'group' => __('Search Features', 'mxchat'),
3064 - ],
3065 - 'mxchat_handle_image_search_request' => [
3066 - 'label' => __('Brave Image Search', 'mxchat'),
3067 - 'pro_only' => false,
3068 - 'group' => __('Search Features', 'mxchat'),
3069 - ],
3070 - 'mxchat_handle_add_to_cart_intent' => [
3071 - 'label' => __('Add to Cart', 'mxchat'),
3072 - 'pro_only' => true,
3073 - 'group' => __('WooCommerce Features', 'mxchat'),
3074 - ],
3075 - 'mxchat_handle_checkout_intent' => [
3076 - 'label' => __('Proceed to Checkout', 'mxchat'),
3077 - 'pro_only' => true,
3078 - 'group' => __('WooCommerce Features', 'mxchat'),
3079 - ],
3080 - 'mxchat_handle_pdf_discussion' => [
3081 - 'label' => __('Chat with PDF', 'mxchat'),
3082 - 'pro_only' => true,
3083 - 'group' => __('Other Features', 'mxchat'),
3084 - ],
3085 - 'mxchat_handle_product_recommendations' => [
3086 - 'label' => __('Product Recommendations', 'mxchat'),
3087 - 'pro_only' => true,
3088 - 'group' => __('WooCommerce Features', 'mxchat'),
3089 - ],
3090 - 'mxchat_live_agent_handover' => [
3091 - 'label' => __('Live Agent', 'mxchat'),
3092 - 'pro_only' => true,
3093 - 'group' => __('Customer Engagement', 'mxchat'),
3094 - ],
3095 - 'mxchat_handle_switch_to_chatbot_intent' => [
3096 - 'label' => __('Back to Chatbot', 'mxchat'),
3097 - 'pro_only' => true,
3098 - 'group' => __('Customer Engagement', 'mxchat'),
3099 - ],
3100 - ];
3101 -
3102 3689 // Return grouped structure if requested
3103 3690 if ($grouped) {
3104 - $grouped_callbacks = [];
3691 + $grouped_callbacks = array();
3105 3692 foreach ($callbacks as $key => $data) {
3106 - $group_label = $data['group'] ?? __('Other Features', 'mxchat');
3107 - $grouped_callbacks[$group_label][$key] = [
3108 - 'label' => $data['label'],
3109 - 'pro_only' => $data['pro_only'],
3110 - ];
3111 - }
3112 - return $grouped_callbacks;
3113 - }
3693 + $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
3114 3694
3115 - return $callbacks;
3116 -}
3695 + // Ensure we carry forward all the new fields in grouped mode
3696 + $callback_data = array(
3697 + 'label' => $data['label'],
3698 + 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
3699 + 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
3700 + 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
3701 + 'addon' => isset($data['addon']) ? $data['addon'] : false,
3702 + 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
3703 + 'installed' => isset($data['installed']) ? $data['installed'] : true
3704 + );
3117 3705
3706 + $grouped_callbacks[$group_label][$key] = $callback_data;
3707 + }
3118 3708
3709 + // Sort within each group to prioritize free features
3710 + foreach ($grouped_callbacks as $group => $items) {
3711 + $free_items = array();
3712 + $pro_items = array();
3119 3713
3120 -private function mxchat_average_vectors($vectors) {
3121 - $vector_length = count($vectors[0]);
3122 - $sum_vector = array_fill(0, $vector_length, 0);
3714 + foreach ($items as $key => $data) {
3715 + if (isset($data['pro_only']) && $data['pro_only']) {
3716 + $pro_items[$key] = $data;
3717 + } else {
3718 + $free_items[$key] = $data;
3719 + }
3720 + }
3123 3721
3124 - foreach ($vectors as $vector) {
3125 - for ($i = 0; $i < $vector_length; $i++) {
3126 - $sum_vector[$i] += $vector[$i];
3722 + $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
3127 3723 }
3128 - }
3129 3724
3130 - // Divide each component by the number of vectors to get the average
3131 - $num_vectors = count($vectors);
3132 - for ($i = 0; $i < $vector_length; $i++) {
3133 - $sum_vector[$i] /= $num_vectors;
3725 + return $grouped_callbacks;
3134 3726 }
3135 3727
3136 - return $sum_vector;
3728 + return $callbacks;
3137 3729 }
3138 3730
3139 3731 public function mxchat_handle_delete_intent() {
3140 3732 if ( ! current_user_can( 'manage_options' ) ) {
@@ -3150,15 +3742,14 @@
3150 3742
3151 3743 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3152 3744 }
3153 3745
3154 - wp_safe_redirect(admin_url('admin.php?page=mxchat-intents'));
3746 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3155 3747 exit;
3156 3748 }
3157 3749
3158 3750
3159 3751 public function mxchat_page_init() {
3160 - // Register settings
3161 3752 register_setting(
3162 3753 'mxchat_option_group',
3163 3754 'mxchat_options',
3164 3755 array($this, 'mxchat_sanitize')
@@ -3169,10 +3760,10 @@
3169 3760 'mxchat_similarity_threshold',
3170 3761 array(
3171 3762 'type' => 'number',
3172 3763 'sanitize_callback' => function($value) {
3173 - $value = absint($value); // Ensure it's an integer
3174 - return min(max($value, 70), 85); // Enforce range
3764 + $value = absint($value);
3765 + return min(max($value, 20), 95);
3175 3766 },
3176 3767 'default' => 80,
3177 3768 )
3178 3769 );
@@ -3201,38 +3792,113 @@
3201 3792 'mxchat-chatbot',
3202 3793 'mxchat_chatbot_section'
3203 3794 );
3204 3795
3205 -
3206 - // Existing fields...
3207 3796 add_settings_field(
3208 - 'api_key',
3209 - esc_html__('OpenAI API Key', 'mxchat'),
3210 - array($this, 'api_key_callback'),
3797 + 'contextual_awareness_toggle',
3798 + esc_html__('Contextual Awareness', 'mxchat'),
3799 + array($this, 'mxchat_contextual_awareness_callback'),
3211 3800 'mxchat-chatbot',
3212 3801 'mxchat_chatbot_section'
3213 3802 );
3214 3803
3804 + add_settings_field(
3805 + 'api_key',
3806 + esc_html__('OpenAI API Key', 'mxchat'),
3807 + array($this, 'api_key_callback'),
3808 + 'mxchat-chatbot',
3809 + 'mxchat_chatbot_section',
3810 + array(
3811 + 'class' => 'mxchat-setting-row',
3812 + 'data-provider' => 'openai'
3813 + )
3814 + );
3815 +
3816 + add_settings_field(
3817 + 'xai_api_key',
3818 + esc_html__('X.AI API Key', 'mxchat'),
3819 + array($this, 'xai_api_key_callback'),
3820 + 'mxchat-chatbot',
3821 + 'mxchat_chatbot_section',
3822 + array(
3823 + 'class' => 'mxchat-setting-row',
3824 + 'data-provider' => 'xai'
3825 + )
3826 + );
3827 +
3828 + add_settings_field(
3829 + 'claude_api_key',
3830 + esc_html__('Claude API Key', 'mxchat'),
3831 + array($this, 'claude_api_key_callback'),
3832 + 'mxchat-chatbot',
3833 + 'mxchat_chatbot_section',
3834 + array(
3835 + 'class' => 'mxchat-setting-row',
3836 + 'data-provider' => 'claude'
3837 + )
3838 + );
3839 +
3840 + add_settings_field(
3841 + 'deepseek_api_key',
3842 + esc_html__('DeepSeek API Key', 'mxchat'),
3843 + array($this, 'deepseek_api_key_callback'),
3844 + 'mxchat-chatbot',
3845 + 'mxchat_chatbot_section',
3846 + array(
3847 + 'class' => 'mxchat-setting-row',
3848 + 'data-provider' => 'deepseek'
3849 + )
3850 + );
3851 +
3852 + add_settings_field(
3853 + 'gemini_api_key',
3854 + esc_html__('Google Gemini API Key', 'mxchat'),
3855 + array($this, 'gemini_api_key_callback'),
3856 + 'mxchat-chatbot',
3857 + 'mxchat_chatbot_section',
3858 + array(
3859 + 'class' => 'mxchat-setting-row',
3860 + 'data-provider' => 'gemini'
3861 + )
3862 + );
3863 +
3864 + add_settings_field(
3865 + 'voyage_api_key',
3866 + esc_html__('Voyage AI API Key', 'mxchat'),
3867 + array($this, 'voyage_api_key_callback'),
3868 + 'mxchat-chatbot',
3869 + 'mxchat_chatbot_section',
3870 + array(
3871 + 'class' => 'mxchat-setting-row',
3872 + 'data-provider' => 'voyage'
3873 + )
3874 + );
3875 +
3215 3876 add_settings_field(
3216 - 'xai_api_key',
3217 - esc_html__('X.AI API Key', 'mxchat'),
3218 - array($this, 'xai_api_key_callback'),
3877 + 'enable_streaming_toggle',
3878 + esc_html__('Enable Streaming', 'mxchat'),
3879 + array($this, 'enable_streaming_toggle_callback'),
3219 3880 'mxchat-chatbot',
3220 - 'mxchat_chatbot_section'
3881 + 'mxchat_chatbot_section', // Same section as your working toggle
3882 + array(
3883 + 'class' => 'mxchat-setting-row streaming-setting',
3884 + 'style' => 'display: none;' // Hidden by default, shown when OpenAI/Claude selected
3885 + )
3221 3886 );
3222 3887
3888 +
3223 3889 add_settings_field(
3224 - 'claude_api_key',
3225 - esc_html__('Claude API Key', 'mxchat'),
3226 - array($this, 'claude_api_key_callback'),
3890 + 'model',
3891 + esc_html__('Chat Model', 'mxchat'),
3892 + array($this, 'mxchat_model_callback'),
3227 3893 'mxchat-chatbot',
3228 3894 'mxchat_chatbot_section'
3229 3895 );
3230 3896
3231 3897 add_settings_field(
3232 - 'deepseek_api_key',
3233 - esc_html__('DeepSeek API Key', 'mxchat'),
3234 - array($this, 'deepseek_api_key_callback'),
3898 + 'embedding_model',
3899 + esc_html__('Embedding Model', 'mxchat'),
3900 + array($this, 'embedding_model_callback'),
3235 3901 'mxchat-chatbot',
3236 3902 'mxchat_chatbot_section'
3237 3903 );
3238 3904
@@ -3243,20 +3909,21 @@
3243 3909 'mxchat-chatbot',
3244 3910 'mxchat_chatbot_section'
3245 3911 );
3246 3912
3913 +
3247 3914 add_settings_field(
3248 - 'model',
3249 - esc_html__('Model', 'mxchat'),
3250 - array($this, 'mxchat_model_callback'),
3915 + 'top_bar_title',
3916 + esc_html__('Top Bar Title', 'mxchat'),
3917 + array($this, 'mxchat_top_bar_title_callback'),
3251 3918 'mxchat-chatbot',
3252 3919 'mxchat_chatbot_section'
3253 3920 );
3254 3921
3255 3922 add_settings_field(
3256 - 'top_bar_title',
3257 - esc_html__('Top Bar Title', 'mxchat'),
3258 - array($this, 'mxchat_top_bar_title_callback'),
3923 + 'ai_agent_text',
3924 + esc_html__('AI Agent Text', 'mxchat'),
3925 + array($this, 'mxchat_ai_agent_text_callback'),
3259 3926 'mxchat-chatbot',
3260 3927 'mxchat_chatbot_section'
3261 3928 );
3262 3929
@@ -3284,48 +3951,40 @@
3284 3951 'mxchat_chatbot_section'
3285 3952 );
3286 3953
3287 3954 add_settings_field(
3288 - 'intro_message',
3289 - esc_html__('Introductory Message', 'mxchat'),
3290 - array($this, 'mxchat_intro_message_callback'),
3955 + 'enable_name_field',
3956 + esc_html__('Require Name Field', 'mxchat'),
3957 + array($this, 'enable_name_field_callback'),
3291 3958 'mxchat-chatbot',
3292 3959 'mxchat_chatbot_section'
3293 3960 );
3294 3961
3295 3962 add_settings_field(
3296 - 'input_copy',
3297 - esc_html__('Input Copy', 'mxchat'),
3298 - array($this, 'mxchat_input_copy_callback'),
3963 + 'name_field_placeholder',
3964 + esc_html__('Name Field Placeholder', 'mxchat'),
3965 + array($this, 'name_field_placeholder_callback'),
3299 3966 'mxchat-chatbot',
3300 3967 'mxchat_chatbot_section'
3301 3968 );
3302 3969
3303 3970 add_settings_field(
3304 - 'rate_limit_logged_out',
3305 - __('Rate Limit for Logged-out Users', 'mxchat'),
3306 - array($this, 'mxchat_rate_limit_logged_out_callback'),
3971 + 'intro_message',
3972 + esc_html__('Introductory Message', 'mxchat'),
3973 + array($this, 'mxchat_intro_message_callback'),
3307 3974 'mxchat-chatbot',
3308 3975 'mxchat_chatbot_section'
3309 3976 );
3310 3977
3311 3978 add_settings_field(
3312 - 'rate_limit_roles',
3313 - __('Rate Limits by User Role', 'mxchat'),
3314 - array($this, 'mxchat_rate_limit_roles_callback'),
3979 + 'input_copy',
3980 + esc_html__('Input Copy', 'mxchat'),
3981 + array($this, 'mxchat_input_copy_callback'),
3315 3982 'mxchat-chatbot',
3316 3983 'mxchat_chatbot_section'
3317 3984 );
3318 3985
3319 3986 add_settings_field(
3320 - 'rate_limit_message',
3321 - esc_html__('Rate Limit Message', 'mxchat'),
3322 - array($this, 'mxchat_rate_limit_message_callback'),
3323 - 'mxchat-chatbot',
3324 - 'mxchat_chatbot_section'
3325 - );
3326 -
3327 - add_settings_field(
3328 3987 'pre_chat_message',
3329 3988 esc_html__('Chat Teaser Pop-up', 'mxchat'),
3330 3989 array($this, 'mxchat_pre_chat_message_callback'),
3331 3990 'mxchat-chatbot',
@@ -3365,9 +4024,9 @@
3365 4024 );
3366 4025
3367 4026 add_settings_field(
3368 4027 'popular_question_1',
3369 - esc_html__('Popular Question 1', 'mxchat'),
4028 + esc_html__('Quick Question 1', 'mxchat'),
3370 4029 array($this, 'mxchat_popular_question_1_callback'),
3371 4030 'mxchat-chatbot',
3372 4031 'mxchat_chatbot_section'
3373 4032 );
@@ -3373,9 +4032,9 @@
3373 4032 );
3374 4033
3375 4034 add_settings_field(
3376 4035 'popular_question_2',
3377 - esc_html__('Popular Question 2', 'mxchat'),
4036 + esc_html__('Quick Question 2', 'mxchat'),
3378 4037 array($this, 'mxchat_popular_question_2_callback'),
3379 4038 'mxchat-chatbot',
3380 4039 'mxchat_chatbot_section'
3381 4040 );
@@ -3381,9 +4040,9 @@
3381 4040 );
3382 4041
3383 4042 add_settings_field(
3384 4043 'popular_question_3',
3385 - esc_html__('Popular Question 3', 'mxchat'),
4044 + esc_html__('Quick Question 3', 'mxchat'),
3386 4045 array($this, 'mxchat_popular_question_3_callback'),
3387 4046 'mxchat-chatbot',
3388 4047 'mxchat_chatbot_section'
3389 4048 );
@@ -3389,20 +4048,21 @@
3389 4048 );
3390 4049
3391 4050 add_settings_field(
3392 4051 'additional_popular_questions',
3393 - esc_html__('Additional Popular Questions', 'mxchat'),
4052 + esc_html__('Additional Quick Questions', 'mxchat'),
3394 4053 array($this, 'mxchat_additional_popular_questions_callback'),
3395 4054 'mxchat-chatbot',
3396 4055 'mxchat_chatbot_section'
3397 4056 );
3398 4057
3399 - // WooCommerce Settings Section
3400 - add_settings_section(
3401 - 'mxchat_woocommerce_section',
3402 - esc_html__('WooCommerce Settings', 'mxchat'),
3403 - null,
3404 - 'mxchat-embed'
4058 +
4059 + add_settings_field(
4060 + 'rate_limits',
4061 + __('Rate Limits Settings', 'mxchat'),
4062 + array($this, 'mxchat_rate_limits_callback'),
4063 + 'mxchat-chatbot',
4064 + 'mxchat_chatbot_section'
3405 4065 );
3406 4066
3407 4067 // Loops Settings Section
3408 4068 add_settings_section(
@@ -3411,33 +4071,8 @@
3411 4071 null,
3412 4072 'mxchat-embed'
3413 4073 );
3414 4074
3415 - // WooCommerce Settings Fields
3416 - add_settings_field(
3417 - 'enable_woocommerce_integration',
3418 - esc_html__('Automatically Embed Products', 'mxchat'),
3419 - array($this, 'mxchat_enable_woocommerce_integration_callback'),
3420 - 'mxchat-embed',
3421 - 'mxchat_woocommerce_section'
3422 - );
3423 -
3424 - add_settings_field(
3425 - 'woocommerce_consumer_key',
3426 - esc_html__('WooCommerce Consumer Key', 'mxchat'),
3427 - array($this, 'mxchat_woocommerce_consumer_key_callback'),
3428 - 'mxchat-embed',
3429 - 'mxchat_woocommerce_section'
3430 - );
3431 -
3432 - add_settings_field(
3433 - 'woocommerce_consumer_secret',
3434 - esc_html__('WooCommerce Consumer Secret', 'mxchat'),
3435 - array($this, 'mxchat_woocommerce_consumer_secret_callback'),
3436 - 'mxchat-embed',
3437 - 'mxchat_woocommerce_section'
3438 - );
3439 -
3440 4075 // Loops Settings Fields
3441 4076 add_settings_field(
3442 4077 'loops_api_key',
3443 4078 esc_html__('Loops API Key', 'mxchat'),
@@ -3541,9 +4176,27 @@
3541 4176 'mxchat-embed',
3542 4177 'mxchat_pdf_intent_section'
3543 4178 );
3544 4179
4180 + // PDF Upload Button Toggle
3545 4181 add_settings_field(
4182 + 'show_pdf_upload_button',
4183 + __('Show PDF Upload Button', 'mxchat'),
4184 + array($this, 'mxchat_show_pdf_upload_button_callback'),
4185 + 'mxchat-embed',
4186 + 'mxchat_pdf_intent_section'
4187 + );
4188 +
4189 + // Word Upload Button Toggle
4190 + add_settings_field(
4191 + 'show_word_upload_button',
4192 + __('Show Word Upload Button', 'mxchat'),
4193 + array($this, 'mxchat_show_word_upload_button_callback'),
4194 + 'mxchat-embed',
4195 + 'mxchat_pdf_intent_section'
4196 + );
4197 +
4198 + add_settings_field(
3546 4199 'pdf_intent_trigger_text',
3547 4200 __('Intent Trigger Text', 'mxchat'),
3548 4201 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3549 4202 'mxchat-embed',
@@ -3608,8 +4261,16 @@
3608 4261 'mxchat_live_agent_section'
3609 4262 );
3610 4263
3611 4264 add_settings_field(
4265 + 'live_agent_user_ids',
4266 + __('Slack Agent User IDs', 'mxchat'),
4267 + array($this, 'mxchat_live_agent_user_ids_callback'),
4268 + 'mxchat-embed',
4269 + 'mxchat_live_agent_section'
4270 + );
4271 +
4272 + add_settings_field(
3612 4273 'live_agent_webhook_url',
3613 4274 __('Slack Webhook URL', 'mxchat'),
3614 4275 array($this, 'mxchat_live_agent_webhook_url_callback'),
3615 4276 'mxchat-embed',
@@ -3638,9 +4299,9 @@
3638 4299
3639 4300 // General Settings Section
3640 4301 add_settings_section(
3641 4302 'mxchat_general_section',
3642 - esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'),
4303 + esc_html__('YouTube Tutorials', 'mxchat'),
3643 4304 null,
3644 4305 'mxchat-general'
3645 4306 );
3646 4307 }
@@ -3645,9 +4306,8 @@
3645 4306 );
3646 4307 }
3647 4308
3648 4309 public function mxchat_prompts_page_init() {
3649 - // Register all settings as a single array
3650 4310 register_setting(
3651 4311 'mxchat_prompts_options',
3652 4312 'mxchat_prompts_options',
3653 4313 array(
@@ -3659,18 +4319,57 @@
3659 4319 'mxchat_use_pinecone' => 0,
3660 4320 'mxchat_pinecone_api_key' => '',
3661 4321 'mxchat_pinecone_environment' => '',
3662 4322 'mxchat_pinecone_index' => '',
3663 - 'mxchat_pinecone_host' => '', // Add this line
4323 + 'mxchat_pinecone_host' => '',
3664 4324 ),
3665 4325 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3666 4326 )
3667 4327 );
3668 4328
3669 - // Add settings saved message
3670 4329 add_action('admin_notices', array($this, 'sync_settings_notice'));
3671 4330 }
3672 4331
4332 +public function mxchat_transcripts_page_init() {
4333 + register_setting(
4334 + 'mxchat_transcripts_options',
4335 + 'mxchat_transcripts_options',
4336 + array(
4337 + 'type' => 'array',
4338 + 'description' => __('MXChat Transcripts Notification Settings', 'mxchat'),
4339 + 'default' => array(
4340 + 'mxchat_enable_notifications' => 0,
4341 + 'mxchat_notification_email' => get_option('admin_email'),
4342 + ),
4343 + 'sanitize_callback' => array($this, 'sanitize_transcripts_options'),
4344 + )
4345 + );
4346 +
4347 + add_settings_section(
4348 + 'mxchat_transcripts_notification_section',
4349 + esc_html__('Chat Notification Settings', 'mxchat'),
4350 + array($this, 'mxchat_transcripts_notification_section_callback'),
4351 + 'mxchat-transcripts'
4352 + );
4353 +
4354 + add_settings_field(
4355 + 'mxchat_enable_notifications',
4356 + esc_html__('Enable Chat Notifications', 'mxchat'),
4357 + array($this, 'mxchat_enable_notifications_callback'),
4358 + 'mxchat-transcripts',
4359 + 'mxchat_transcripts_notification_section'
4360 + );
4361 +
4362 + add_settings_field(
4363 + 'mxchat_notification_email',
4364 + esc_html__('Notification Email Address', 'mxchat'),
4365 + array($this, 'mxchat_notification_email_callback'),
4366 + 'mxchat-transcripts',
4367 + 'mxchat_transcripts_notification_section'
4368 + );
4369 +}
4370 +
4371 +
3673 4372 /**
3674 4373 * Sanitize all prompts options
3675 4374 *
3676 4375 * @param array $input The unsanitized options array
@@ -3752,183 +4451,130 @@
3752 4451 public function sanitize_sync_setting($input) {
3753 4452 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3754 4453 }
3755 4454
4455 +public function mxchat_rate_limits_callback() {
4456 + $all_options = get_option('mxchat_options', []);
3756 4457
4458 + // Define available rate limits
4459 + $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
3757 4460
3758 -public function mxchat_handle_activate_license() {
3759 - // Check nonce
3760 - if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
3761 - wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
3762 - return;
3763 - }
4461 + // Define available timeframes
4462 + $timeframes = array(
4463 + 'hourly' => __('Per Hour', 'mxchat'),
4464 + 'daily' => __('Per Day', 'mxchat'),
4465 + 'weekly' => __('Per Week', 'mxchat'),
4466 + 'monthly' => __('Per Month', 'mxchat')
4467 + );
3764 4468
3765 - // Verify user capabilities
3766 - if (!current_user_can('manage_options')) {
3767 - wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
3768 - return;
3769 - }
4469 + // Get all roles plus a "logged_out" pseudo-role
4470 + $roles = wp_roles()->get_names();
4471 + $roles['logged_out'] = __('Logged Out Users', 'mxchat');
3770 4472
3771 - $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
3772 - $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
4473 + // Start the wrapper
4474 + echo '<div class="pro-feature-wrapper active">';
4475 + echo '<div class="mxchat-rate-limits-container">';
3773 4476
3774 - if (empty($license_key) || empty($customer_email)) {
3775 - wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
3776 - return;
3777 - }
4477 + echo '<p class="description" style="margin-bottom: 20px;">' .
4478 + esc_html__('Set message limits for each user role and customize the experience when users reach those limits. You can use {limit}, {timeframe}, {count}, and {remaining} as placeholders.', 'mxchat') .
4479 + '</p>';
3778 4480
3779 - $product_id = 'MxChatPRO';
3780 - $response = wp_remote_get(
3781 - add_query_arg(
3782 - array(
3783 - 'wc-api' => 'software-api',
3784 - 'request' => 'activation',
3785 - 'email' => $customer_email,
3786 - 'license_key' => $license_key,
3787 - 'product_id' => $product_id
3788 - ),
3789 - 'http://mxchat.ai/'
3790 - )
3791 - );
4481 + // Add markdown link documentation
4482 + echo '<div class="notice notice-info inline" style="margin-bottom: 20px; padding: 10px;">';
4483 + echo '<p><strong>' . esc_html__('Markdown Links Supported:', 'mxchat') . '</strong></p>';
4484 + echo '<p>' . esc_html__('You can include clickable links in your custom messages using markdown syntax:', 'mxchat') . '</p>';
4485 + echo '<ul style="margin-left: 20px;">';
4486 + echo '<li><code>[Link text](https://example.com)</code> - Creates a clickable link</li>';
4487 + echo '<li><code>[Visit our pricing](https://example.com/pricing)</code> - Link with custom text</li>';
4488 + echo '<li><code>Plain URLs like https://example.com will also become clickable</code></li>';
4489 + echo '</ul>';
4490 + echo '</div>';
3792 4491
3793 - if (is_wp_error($response)) {
3794 - wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message());
3795 - return;
3796 - }
4492 + // Output the controls for each role
4493 + foreach ($roles as $role_id => $role_name) {
4494 + // Get saved options or defaults
4495 + $default_limit = ($role_id === 'logged_out') ? '10' : '100';
4496 + $default_timeframe = 'daily';
4497 + $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
3797 4498
3798 - $body = wp_remote_retrieve_body($response);
3799 - $data = json_decode($body);
4499 + $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
4500 + ? $all_options['rate_limits'][$role_id]['limit']
4501 + : $default_limit;
3800 4502
3801 - if ($data && isset($data->activated) && $data->activated) {
3802 - update_option('mxchat_license_status', 'active');
3803 - update_option('mxchat_pro_email', $customer_email);
3804 - update_option('mxchat_activation_key', $license_key);
3805 - wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
3806 - } else {
3807 - $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
3808 - update_option('mxchat_license_status', 'inactive');
3809 - update_option('mxchat_license_error', $error_message);
3810 - wp_send_json_error($error_message);
3811 - }
3812 -}
4503 + $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
4504 + ? $all_options['rate_limits'][$role_id]['timeframe']
4505 + : $default_timeframe;
3813 4506
4507 + $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
4508 + ? $all_options['rate_limits'][$role_id]['message']
4509 + : $default_message;
3814 4510
3815 -public function mxchat_rate_limit_logged_out_callback() {
3816 - // Load the entire 'mxchat_options' array
3817 - $all_options = get_option('mxchat_options', []);
4511 + // Output the row
4512 + echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
3818 4513
3819 - // Retrieve the saved rate limit or use the default value
3820 - $default_rate_limit = '10';
3821 - $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit;
4514 + // Role label
4515 + echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
3822 4516
3823 - // Define available rate limits
3824 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
4517 + // Controls section
4518 + echo '<div class="mxchat-rate-limit-controls-wrapper">';
3825 4519
3826 - // Output the dropdown
3827 - echo '<div class="pro-feature-wrapper active">';
3828 - echo '<select id="rate_limit_logged_out" name="rate_limit_logged_out">';
3829 - foreach ($rate_limits as $limit) {
3830 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3831 - }
3832 - echo '</select>';
3833 - 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>';
3834 - echo '</div>';
3835 -}
4520 + // Rate limit and timeframe controls
4521 + echo '<div class="mxchat-rate-limit-controls">';
3836 4522
3837 -// Add this new callback function
3838 -public function mxchat_rate_limit_roles_callback() {
3839 - $all_options = get_option('mxchat_options', []);
3840 - $roles = wp_roles()->get_names();
3841 - $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
3842 -
3843 - echo '<div class="pro-feature-wrapper active mxchat-autosave-section">';
3844 -
3845 - foreach ($roles as $role_id => $role_name) {
3846 - $default_rate_limit = '100';
3847 - $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id])
3848 - ? $all_options['role_rate_limits'][$role_id]
3849 - : $default_rate_limit;
3850 -
3851 - echo '<div style="margin-bottom: 10px;">';
3852 - echo '<label style="display: inline-block; width: 150px;">' . esc_html($role_name) . ':</label>';
4523 + // Limit dropdown
4524 + echo '<div>';
4525 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
3853 4526 echo '<select
3854 - id="role_rate_limits_' . esc_attr($role_id) . '"
3855 - name="mxchat_options[role_rate_limits][' . esc_attr($role_id) . ']"
4527 + id="rate_limits_' . esc_attr($role_id) . '_limit"
4528 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
3856 4529 class="mxchat-autosave-field">';
3857 4530 foreach ($rate_limits as $limit) {
3858 - echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_rate_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
4531 + echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3859 4532 }
3860 4533 echo '</select>';
3861 4534 echo '</div>';
3862 - }
3863 4535
3864 - 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>';
3865 - echo '</div>';
3866 -}
4536 + // Timeframe dropdown
4537 + echo '<div>';
4538 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
4539 + echo '<select
4540 + id="rate_limits_' . esc_attr($role_id) . '_timeframe"
4541 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
4542 + class="mxchat-autosave-field">';
4543 + foreach ($timeframes as $value => $label) {
4544 + echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
4545 + }
4546 + echo '</select>';
4547 + echo '</div>';
3867 4548
3868 -public function mxchat_rate_limit_message_callback() {
3869 - // Load the entire 'mxchat_options' array
3870 - $all_options = get_option('mxchat_options', []);
4549 + echo '</div>'; // End controls
3871 4550
3872 - // Retrieve the saved message or use the default value
3873 - $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat');
3874 - $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message;
4551 + // Custom message textarea
4552 + echo '<div class="mxchat-rate-limit-message">';
4553 + echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
4554 + echo '<textarea
4555 + id="rate_limits_' . esc_attr($role_id) . '_message"
4556 + name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
4557 + class="mxchat-autosave-field"
4558 + placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
4559 + esc_textarea($custom_message) .
4560 + '</textarea>';
4561 + echo '<p class="description">' .
4562 + esc_html__('Example: Rate limit reached! [Visit our pricing page](https://example.com/pricing) to upgrade.', 'mxchat') .
4563 + '</p>';
4564 + echo '</div>'; // End message
3875 4565
3876 - // Output the textarea
3877 - echo '<div class="pro-feature-wrapper active">';
3878 - printf(
3879 - '<textarea id="rate_limit_message" name="rate_limit_message" rows="3" cols="50">%s</textarea>',
3880 - esc_textarea($rate_limit_message)
3881 - );
3882 - echo '<p class="description">' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '</p>';
3883 - echo '</div>';
3884 -}
4566 + echo '</div>'; // End controls wrapper
3885 4567
4568 + echo '</div>'; // End row
4569 + }
3886 4570
3887 -public function mxchat_enable_woocommerce_integration_callback() {
3888 - // Load from mxchat_options array
3889 - $options = get_option('mxchat_options', []);
4571 + echo '</div>'; // End container
3890 4572
3891 - // Get WooCommerce integration status with backwards compatibility
3892 - $woo_integration = isset($options['enable_woocommerce_integration'])
3893 - ? $options['enable_woocommerce_integration']
3894 - : '';
3895 -
3896 - // Support both old ('1') and new ('on') values
3897 - $checked = ($woo_integration === 'on' || $woo_integration === '1') ? 'checked' : '';
3898 -
3899 - // Check if the feature is activated (paid feature)
3900 - $disabled = $this->is_activated ? '' : 'disabled';
3901 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3902 -
3903 - echo '<div class="' . esc_attr($class) . '">';
3904 -
3905 - echo '<label class="toggle-switch">';
3906 - echo sprintf(
3907 - '<input type="checkbox" id="enable_woocommerce_integration" name="enable_woocommerce_integration" value="on" %s %s />',
3908 - esc_attr($checked),
3909 - esc_attr($disabled)
3910 - );
3911 - echo '<span class="slider"></span>';
3912 - echo '</label>';
3913 -
3914 - echo '<p class="description">' . esc_html__('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.', 'mxchat') . '</p>';
3915 -
3916 - // Pro feature overlay for non-activated users
3917 - if (!$this->is_activated) {
3918 - echo '<div class="pro-feature-overlay">';
3919 - echo '<a href="https://mxchat.ai/" target="_blank">';
3920 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
3921 - echo '</a>';
3922 - echo '</div>';
3923 - }
3924 -
3925 - echo '</div>';
4573 + echo '</div>'; // End pro-feature-wrapper
3926 4574 }
3927 4575
3928 4576
3929 -
3930 -
3931 4577 private function mxchat_add_option_field($id, $title, $callback = '') {
3932 4578 add_settings_field(
3933 4579 $id,
3934 4580 __($title, 'mxchat'),
@@ -3938,162 +4584,89 @@
3938 4584 $id === 'model' ? ['label_for' => 'model'] : []
3939 4585 );
3940 4586 }
3941 4587
4588 +// OpenAI API Key
3942 4589 public function api_key_callback() {
3943 - // Retrieve from your stored 'api_key' in the mxchat_options array
3944 4590 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
3945 4591
3946 - // Notice we changed the name to "api_key" (no array notation).
3947 - echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" />';
4592 + echo '<div class="api-key-wrapper" data-provider="openai">';
4593 + echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
3948 4594 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3949 - echo '<p class="description">' . esc_html__('The OpenAI API key is required even when using other models as it is used for vector embedding functionality. You must add credits to your OpenAI billing account before use.', 'mxchat') . '</p>';
4595 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4596 + echo '</div>';
3950 4597 }
4598 +
4599 +// X.AI API Key
3951 4600 public function xai_api_key_callback() {
3952 - // Check if the feature is activated (paid feature)
3953 - $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated
3954 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status
3955 -
3956 - // Retrieve the X.AI API key value
3957 4601 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
3958 4602
3959 - // Render the input field for the X.AI API key
3960 - echo '<div class="' . esc_attr($class) . '">';
3961 - printf(
3962 - '<input type="password" id="xai_api_key" name="xai_api_key" value="%s" class="regular-text" %s />',
3963 - $xaiApiKey,
3964 - $disabled
3965 - );
4603 + echo '<div class="api-key-wrapper" data-provider="xai">';
4604 + echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
3966 4605 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3967 -
3968 - // If the feature is not activated, show the overlay with a "Pro Only" message
3969 - if (!$this->is_activated) {
3970 - echo '<div class="pro-feature-overlay">';
3971 - 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>';
3972 - echo '</div>';
3973 - }
3974 -
4606 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3975 4607 echo '</div>';
3976 4608 }
4609 +// Claude API Key
3977 4610 public function claude_api_key_callback() {
3978 - // Check if the feature is activated (paid feature)
3979 - $disabled = $this->is_activated ? '' : 'disabled';
3980 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
3981 -
3982 - // Retrieve the Claude API key value directly like the others
3983 4611 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
3984 -
3985 - // Use direct name field like the other working API keys
3986 - echo '<div class="' . esc_attr($class) . '">';
3987 - printf(
3988 - '<input type="password" id="claude_api_key" name="claude_api_key" value="%s" class="regular-text" %s />',
3989 - $claudeApiKey,
3990 - $disabled
3991 - );
4612 +
4613 + echo '<div class="api-key-wrapper" data-provider="claude">';
4614 + echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
3992 4615 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
3993 -
3994 - if (!$this->is_activated) {
3995 - echo '<div class="pro-feature-overlay">';
3996 - 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>';
3997 - echo '</div>';
3998 - }
4616 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
3999 4617 echo '</div>';
4000 4618 }
4619 +
4620 +// DeepSeek API Key
4001 4621 public function deepseek_api_key_callback() {
4002 - // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array
4003 4622 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4004 4623
4005 - // Notice we changed the name to "deepseek_api_key" (no array notation).
4006 - echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" />';
4624 + echo '<div class="api-key-wrapper" data-provider="deepseek">';
4625 + echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4007 4626 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4627 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4628 + echo '</div>';
4008 4629 }
4009 4630
4631 +// Gemini API Key
4632 +public function gemini_api_key_callback() {
4633 + $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
4010 4634
4011 -public function mxchat_woocommerce_consumer_key_callback() {
4012 - // Load the entire 'mxchat_options' array
4013 - $all_options = get_option('mxchat_options', []);
4014 -
4015 - // Retrieve the WooCommerce consumer key or set a default
4016 - $consumer_key = isset($all_options['woocommerce_consumer_key']) ? $all_options['woocommerce_consumer_key'] : '';
4017 -
4018 - // Check if the feature is activated (paid feature)
4019 - $disabled = $this->is_activated ? '' : 'disabled';
4020 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4021 -
4022 - echo '<div class="' . esc_attr($class) . '">';
4023 -
4024 - // Render the input field
4025 - printf(
4026 - '<input type="text" id="woocommerce_consumer_key" name="woocommerce_consumer_key" value="%s" class="regular-text" %s />',
4027 - esc_attr($consumer_key),
4028 - esc_attr($disabled)
4029 - );
4030 -
4031 - // Description for the input field
4032 - echo '<p class="description">' . esc_html__('Enter your WooCommerce consumer key for integration.', 'mxchat') . '</p>';
4033 -
4034 - // Pro feature overlay for non-activated users
4035 - if (!$this->is_activated) {
4036 - echo '<div class="pro-feature-overlay">';
4037 - 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>';
4038 - echo '</div>';
4039 - }
4040 -
4635 + echo '<div class="api-key-wrapper" data-provider="gemini">';
4636 + echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
4637 + echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4638 + echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
4041 4639 echo '</div>';
4042 4640 }
4043 4641
4044 -public function mxchat_woocommerce_consumer_secret_callback() {
4045 - // Get value with fallback
4046 - $consumer_secret = isset($this->options['woocommerce_consumer_secret'])
4047 - ? esc_attr($this->options['woocommerce_consumer_secret'])
4048 - : '';
4642 +// Voyage API Key
4643 +public function voyage_api_key_callback() {
4644 + $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4049 4645
4050 - // Check if the feature is activated (paid feature)
4051 - $disabled = $this->is_activated ? '' : 'disabled';
4052 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4053 -
4054 - echo '<div class="' . esc_attr($class) . '">';
4055 -
4056 - // Output the password input
4057 - echo sprintf(
4058 - '<input type="password"
4059 - id="woocommerce_consumer_secret"
4060 - name="woocommerce_consumer_secret"
4061 - value="%s"
4062 - class="regular-text"
4063 - %s />',
4064 - $consumer_secret,
4065 - esc_attr($disabled)
4066 - );
4067 -
4068 - // Add show/hide button
4069 - echo '<button type="button" id="toggleWooCommerceSecretVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4070 -
4071 - // Pro feature overlay
4072 - if (!$this->is_activated) {
4073 - echo '<div class="pro-feature-overlay">';
4074 - echo '<a href="https://mxchat.ai/" target="_blank">';
4075 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4076 - echo '</a>';
4077 - echo '</div>';
4078 - }
4079 -
4646 + echo '<div class="api-key-wrapper" data-provider="voyage">';
4647 + echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4648 + echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4649 + echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
4080 4650 echo '</div>';
4081 4651 }
4652 +
4082 4653 public function mxchat_loops_api_key_callback() {
4083 - // Support both old and new format
4084 4654 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4085 4655
4086 - echo '<div class="api-key-wrapper">';
4656 + // Hidden fields to "trap" autofill
4657 + echo '<input type="text" style="display:none" autocomplete="username" />';
4658 + echo '<input type="password" style="display:none" autocomplete="current-password" />';
4659 +
4660 + echo '<div class="api-key-wrapper" data-provider="loops">';
4087 4661 echo sprintf(
4088 - '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" />',
4662 + '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
4089 4663 $loops_api_key
4090 4664 );
4091 4665 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4092 4666 echo '</div>';
4093 - echo '<p class="description">' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '</p>';
4667 + echo '<p class="description">' . esc_html__('Enter your Loops API Key here. Once entered, refreshed page to load list (See FAQ for details)', 'mxchat') . '</p>';
4094 4668 }
4095 -
4096 4669 public function mxchat_loops_mailing_list_callback() {
4097 4670 // Add error handling and type checking
4098 4671 $loops_api_key = '';
4099 4672 $selected_list = '';
@@ -4111,8 +4684,12 @@
4111 4684 if (!empty($loops_api_key)) {
4112 4685 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4113 4686 if (is_array($lists) && !empty($lists)) {
4114 4687 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
4688 +
4689 + // Add a default "Select a list" option
4690 + echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
4691 +
4115 4692 foreach ($lists as $list) {
4116 4693 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4117 4694 echo sprintf(
4118 4695 '<option value="%s" %s>%s</option>',
@@ -4122,8 +4699,9 @@
4122 4699 );
4123 4700 }
4124 4701 }
4125 4702 echo '</select>';
4703 + echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
4126 4704 } else {
4127 4705 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4128 4706 }
4129 4707 } else {
@@ -4134,14 +4712,13 @@
4134 4712 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4135 4713 $triggered_response = isset($this->options['triggered_phrase_response'])
4136 4714 ? $this->options['triggered_phrase_response']
4137 4715 : $default_response;
4138 -
4139 4716 echo sprintf(
4140 4717 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4141 4718 esc_textarea($triggered_response)
4142 4719 );
4143 - 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>';
4720 + echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a trigger keyword is detected. The AI will use this as guidance to naturally ask for the user\'s email in a conversational way.', 'mxchat') . '</p>';
4144 4721 }
4145 4722
4146 4723 public function mxchat_email_capture_response_callback() {
4147 4724 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
@@ -4147,16 +4724,14 @@
4147 4724 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4148 4725 $email_capture_response = isset($this->options['email_capture_response'])
4149 4726 ? $this->options['email_capture_response']
4150 4727 : $default_response;
4151 -
4152 4728 echo sprintf(
4153 4729 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4154 4730 esc_textarea($email_capture_response)
4155 4731 );
4156 - echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
4732 + echo '<p class="description">' . esc_html__('Enter the instruction for the AI when a user provides their email. The AI will use this as guidance to naturally confirm the email capture in a conversational way.', 'mxchat') . '</p>';
4157 4733 }
4158 -
4159 4734 public function mxchat_pre_chat_message_callback() {
4160 4735 // Load the entire 'mxchat_options' array
4161 4736 $all_options = get_option('mxchat_options', []);
4162 4737
@@ -4171,49 +4746,137 @@
4171 4746 );
4172 4747 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>';
4173 4748 }
4174 4749
4175 -
4176 -
4177 4750 // Callback for AI Instructions textarea
4178 4751 public function system_prompt_instructions_callback() {
4179 4752 // Retrieve the current value of the system prompt instructions
4180 4753 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4181 -
4182 4754 // Render the textarea field
4183 4755 printf(
4184 4756 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4185 4757 $instructions
4186 4758 );
4759 + // Provide a helpful description with sample instructions button
4760 + 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>';
4761 + echo '<div class="mxchat-instructions-container">';
4762 + echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
4763 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
4764 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4765 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4766 + echo '</svg>';
4767 + echo esc_html__('View Sample Instructions', 'mxchat');
4768 + echo '</button>';
4769 + echo '</div>';
4187 4770
4188 - // Provide a helpful description
4189 - 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>';
4771 + // Add modal to WordPress admin footer instead of inline
4772 + add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
4190 4773 }
4191 4774
4775 +// New method to render modal in admin footer
4776 +public function render_sample_instructions_modal() {
4777 + static $modal_rendered = false;
4778 + if ($modal_rendered) return; // Prevent duplicate modals
4779 + $modal_rendered = true;
4192 4780
4781 + echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
4782 + echo '<div class="mxchat-instructions-modal-content">';
4783 + echo '<div class="mxchat-instructions-modal-header">';
4784 + echo '<h3 class="mxchat-instructions-modal-title">';
4785 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
4786 + echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4787 + echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4788 + echo '</svg>';
4789 + echo esc_html__('Sample AI Instructions', 'mxchat');
4790 + echo '</h3>';
4791 + echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
4792 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
4793 + echo '<line x1="18" y1="6" x2="6" y2="18"/>';
4794 + echo '<line x1="6" y1="6" x2="18" y2="18"/>';
4795 + echo '</svg>';
4796 + echo '</button>';
4797 + echo '</div>';
4798 + echo '<div class="mxchat-instructions-modal-body">';
4799 + echo '<div class="mxchat-instructions-content">';
4800 + echo esc_html('You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
4801 +
4802 +# Response Style - CRITICALLY IMPORTANT
4803 +- MAXIMUM LENGTH: 1-3 short sentences per response
4804 +- Ultra-concise: Get straight to the answer with no filler
4805 +- No introductions like "Sure!" or "I\'d be happy to help"
4806 +- No phrases like "based on my knowledge" or "according to information"
4807 +- No explanatory text before giving the answer
4808 +- No summaries or repetition
4809 +- Hyperlink all URLs
4810 +- Respond in user\'s language
4811 +- Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
4812 +
4813 +# Knowledge Base Requirements - PREVENT HALLUCINATIONS
4814 +- ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
4815 +- If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
4816 +- NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
4817 +- When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
4818 +- Better to admit insufficient information than provide inaccurate answers');
4819 + echo '</div>';
4820 + echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
4821 + echo '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
4822 + echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
4823 + echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
4824 + echo '</svg>';
4825 + echo esc_html__('Copy Instructions', 'mxchat');
4826 + echo '</button>';
4827 + echo '</div>';
4828 + echo '<div class="mxchat-instructions-modal-footer">';
4829 + echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
4830 + echo '</div>';
4831 + echo '</div>';
4832 + echo '</div>';
4833 +}
4834 +
4835 +
4193 4836 public function mxchat_model_callback() {
4194 4837 // Define available models grouped by provider
4195 4838 $models = array(
4839 + esc_html__('Google Gemini Models', 'mxchat') => array(
4840 + 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
4841 + 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
4842 + 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
4843 + 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
4844 + ),
4196 4845 esc_html__('X.AI Models', 'mxchat') => array(
4197 - 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'),
4198 - 'grok-2' => esc_html__('Grok 2', 'mxchat')
4846 + 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
4847 + 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
4848 + 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
4849 + 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
4850 + 'grok-2' => esc_html__('Grok 2', 'mxchat'),
4851 + 'grok-4-0709' => esc_html__('Grok 4 (Latest Flagship)', 'mxchat'), // Added new Grok-4 model
4199 4852 ),
4200 4853 esc_html__('DeepSeek Models', 'mxchat') => array(
4201 4854 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4202 4855 ),
4203 4856 esc_html__('Claude Models', 'mxchat') => array(
4204 - 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'),
4205 - 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'),
4857 + 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
4858 + 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
4859 + 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
4860 + 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Intelligent)', 'mxchat'),
4861 + 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
4206 4862 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4207 - 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4863 + 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat'),
4208 4864 ),
4209 4865 esc_html__('OpenAI Models', 'mxchat') => array(
4866 + // NEW: GPT-5 family
4867 + 'gpt-5' => esc_html__('GPT-5 (Flagship for Coding, Reasoning & Agents)', 'mxchat'),
4868 + 'gpt-5-mini' => esc_html__('GPT-5 Mini (Faster, Cost-Efficient for Precise Prompts)', 'mxchat'),
4869 + 'gpt-5-nano' => esc_html__('GPT-5 Nano (Fastest & Cheapest for Summarization/Classification)', 'mxchat'),
4870 +
4871 + // Existing OpenAI models
4872 + 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4210 4873 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4211 4874 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4212 4875 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4213 4876 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4214 - 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4215 - )
4877 + 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat'),
4878 + ),
4216 4879 );
4217 4880
4218 4881 // Retrieve the currently selected model from saved options
4219 4882 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
@@ -4218,9 +4881,9 @@
4218 4881 // Retrieve the currently selected model from saved options
4219 4882 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4220 4883
4221 4884 // Begin the select dropdown
4222 - echo '<select id="model" name="model">'; // No array notation in name attribute
4885 + echo '<select id="model" name="model">';
4223 4886
4224 4887 // Iterate over groups of models
4225 4888 foreach ($models as $group_label => $group_models) {
4226 4889 echo '<optgroup label="' . esc_attr($group_label) . '">';
@@ -4225,14 +4888,10 @@
4225 4888 foreach ($models as $group_label => $group_models) {
4226 4889 echo '<optgroup label="' . esc_attr($group_label) . '">';
4227 4890
4228 4891 foreach ($group_models as $model_value => $model_label) {
4229 - // Disable Pro-only models for non-activated users
4230 - $disabled = (!$this->is_activated && ($group_label === esc_html__('X.AI Models', 'mxchat') || $group_label === esc_html__('Claude Models', 'mxchat'))) ? 'disabled' : '';
4231 - $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') : '';
4232 -
4233 - // Output the option element
4234 - echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . ' ' . $disabled . '>' . esc_html($model_label . $label_suffix) . '</option>';
4892 + // All models enabled - no disabled attribute or Pro Only label
4893 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4235 4894 }
4236 4895
4237 4896 echo '</optgroup>';
4238 4897 }
@@ -4239,14 +4898,70 @@
4239 4898
4240 4899 // Close the select dropdown
4241 4900 echo '</select>';
4242 4901
4243 - // Add a description below the dropdown
4244 - echo '<p class="description">' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '</p>';
4902 + // Updated description to remove mention of Pro-only models
4903 + echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
4245 4904 }
4246 4905
4247 4906
4907 +// Update your existing callback method
4908 +public function enable_streaming_toggle_callback() {
4909 + // Get value from options array, default to 'on'
4910 + $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
4911 + $checked = ($enabled === 'on') ? 'checked' : '';
4248 4912
4913 + echo '<label class="toggle-switch">';
4914 + echo sprintf(
4915 + '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
4916 + esc_attr($checked)
4917 + );
4918 + echo '<span class="slider"></span>';
4919 + echo '</label>';
4920 + echo '<p class="description">' .
4921 + esc_html__('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, and Grok). When disabled, responses will load all at once.', 'mxchat') .
4922 + '</p>';
4923 +
4924 + // Test button with better styling
4925 + echo '<div style="margin-top: 15px;">';
4926 + echo '<button type="button" id="mxchat-test-streaming-btn" class="button button-secondary">';
4927 + echo '<span class="dashicons dashicons-admin-tools" style="vertical-align: middle; margin-right: 5px;"></span>';
4928 + echo esc_html__('Test Streaming Compatibility', 'mxchat');
4929 + echo '</button>';
4930 + echo '<p id="mxchat-test-streaming-result" style="margin-top:10px; font-weight: bold;"></p>';
4931 + echo '</div>';
4932 +}
4933 +
4934 +
4935 +// Callback function for embedding model selection
4936 +public function embedding_model_callback() {
4937 + $models = array(
4938 + esc_html__('OpenAI Embeddings', 'mxchat') => array(
4939 + 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
4940 + 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
4941 + 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
4942 + ),
4943 + esc_html__('Voyage AI Embeddings', 'mxchat') => array(
4944 + 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
4945 + ),
4946 + esc_html__('Google Gemini Embeddings', 'mxchat') => array(
4947 + 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
4948 + )
4949 + );
4950 + $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
4951 + echo '<select id="embedding_model" name="embedding_model">';
4952 + foreach ($models as $group_label => $group_models) {
4953 + echo '<optgroup label="' . esc_attr($group_label) . '">';
4954 + foreach ($group_models as $model_value => $model_label) {
4955 + echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4956 + }
4957 + echo '</optgroup>';
4958 + }
4959 + echo '</select>';
4960 + 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>';
4961 +}
4962 +
4963 +
4249 4964 public function mxchat_top_bar_title_callback() {
4250 4965 // Retrieve the current value of the top bar title from saved options
4251 4966 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4252 4967
@@ -4255,9 +4970,16 @@
4255 4970
4256 4971 // Add a description
4257 4972 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4258 4973 }
4259 -
4974 +public function mxchat_ai_agent_text_callback() {
4975 + // Retrieve the current value of the AI agent text from saved options
4976 + $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
4977 + // Render the input field
4978 + echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
4979 + // Add a description
4980 + echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
4981 +}
4260 4982 public function enable_email_block_callback() {
4261 4983 // Load full plugin options array
4262 4984 $all_options = get_option('mxchat_options', []);
4263 4985
@@ -4277,9 +4999,8 @@
4277 4999 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>';
4278 5000 }
4279 5001
4280 5002
4281 -
4282 5003 public function email_blocker_header_content_callback() {
4283 5004 // Load the entire 'mxchat_options' array
4284 5005 $all_options = get_option('mxchat_options', []);
4285 5006
@@ -4287,14 +5008,15 @@
4287 5008 $content = isset($all_options['email_blocker_header_content'])
4288 5009 ? $all_options['email_blocker_header_content']
4289 5010 : '';
4290 5011
4291 - // Render the textarea
5012 + // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4292 5013 echo '<textarea
4293 5014 id="email_blocker_header_content"
4294 5015 name="email_blocker_header_content"
4295 5016 rows="5"
4296 5017 cols="70"
5018 + data-setting="email_blocker_header_content"
4297 5019 >' . esc_textarea($content) . '</textarea>';
4298 5020
4299 5021 echo '<p class="description">';
4300 5022 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');
@@ -4300,9 +5022,8 @@
4300 5022 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');
4301 5023 echo '</p>';
4302 5024 }
4303 5025
4304 -
4305 5026 public function email_blocker_button_text_callback() {
4306 5027 // Load the entire 'mxchat_options' array
4307 5028 $all_options = get_option('mxchat_options', []);
4308 5029
@@ -4319,27 +5040,55 @@
4319 5040 echo '</p>';
4320 5041 }
4321 5042
4322 5043
5044 +//Enable name field callback
5045 +public function enable_name_field_callback() {
5046 + // Load full plugin options array
5047 + $all_options = get_option('mxchat_options', []);
5048 + // Get the value, default to 'off'
5049 + $enable_name_field = isset($all_options['enable_name_field']) ? $all_options['enable_name_field'] : 'off';
5050 + // Check if it's 'on'
5051 + $checked = ($enable_name_field === 'on') ? 'checked' : '';
5052 + echo '<label class="toggle-switch">';
5053 + echo sprintf(
5054 + '<input type="checkbox" id="enable_name_field" name="enable_name_field" value="on" %s />',
5055 + esc_attr($checked)
5056 + );
5057 + echo '<span class="slider"></span>';
5058 + echo '</label>';
5059 + echo '<p class="description">' . esc_html__('Enable this to also require users to enter their name along with their email before chatting.', 'mxchat') . '</p>';
5060 +}
5061 +//Name field placeholder callback
5062 +public function name_field_placeholder_callback() {
5063 + $all_options = get_option('mxchat_options', []);
5064 + $placeholder = isset($all_options['name_field_placeholder'])
5065 + ? $all_options['name_field_placeholder']
5066 + : esc_html__('Enter your name', 'mxchat');
4323 5067
5068 + echo '<input type="text" id="name_field_placeholder" name="name_field_placeholder" value="' . esc_attr($placeholder) . '" style="width: 300px;" />';
5069 + echo '<p class="description">';
5070 + echo esc_html__('Placeholder text for the name field.', 'mxchat');
5071 + echo '</p>';
5072 +}
5073 +
5074 +
5075 +
4324 5076 public function mxchat_intro_message_callback() {
4325 5077 // Load the entire 'mxchat_options' array
4326 5078 $all_options = get_option('mxchat_options', []);
4327 -
4328 5079 // Retrieve the saved intro message or use the default
4329 5080 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4330 5081 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4331 -
4332 - // Output the textarea with the saved value
5082 + // Output the textarea with the saved value without escaping HTML
4333 5083 ?>
4334 - <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo esc_textarea($saved_message); ?></textarea>
5084 + <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4335 5085 <p class="description">
4336 - <?php esc_html_e('Enter your message. Line breaks will be preserved.', 'mxchat'); ?>
5086 + <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4337 5087 </p>
4338 5088 <?php
4339 5089 }
4340 5090
4341 -
4342 5091 public function mxchat_input_copy_callback() {
4343 5092 // Load the entire 'mxchat_options' array
4344 5093 $all_options = get_option('mxchat_options', []);
4345 5094
@@ -4805,8 +5554,23 @@
4805 5554 '</p>';
4806 5555
4807 5556 }
4808 5557
5558 +public function mxchat_contextual_awareness_callback() {
5559 + // Get value from options array, default to 'off'
5560 + $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
5561 + $checked = ($contextual_awareness === 'on') ? 'checked' : '';
5562 + echo '<label class="toggle-switch">';
5563 + echo sprintf(
5564 + '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
5565 + esc_attr($checked)
5566 + );
5567 + echo '<span class="slider"></span>';
5568 + echo '</label>';
5569 + echo '<p class="description">' .
5570 + esc_html__('Enable contextual awareness to allow the chatbot to understand and reference the current page content. When enabled, the chatbot will have access to the page title, content, and URL for more relevant responses.', 'mxchat') .
5571 + '</p>';
5572 +}
4809 5573
4810 5574
4811 5575 public function mxchat_privacy_toggle_callback() {
4812 5576 // Load from mxchat_options array
@@ -4909,13 +5673,13 @@
4909 5673 // Render the input field
4910 5674 printf(
4911 5675 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
4912 5676 esc_attr($popular_question_1),
4913 - esc_attr__('Enter Popular Question 1', 'mxchat')
5677 + esc_attr__('Enter Quick Question 1', 'mxchat')
4914 5678 );
4915 5679
4916 5680 // Add a description for the field
4917 - echo '<p class="description">' . esc_html__('This will be the first popular question in the chatbot.', 'mxchat') . '</p>';
5681 + echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
4918 5682 }
4919 5683
4920 5684
4921 5685 public function mxchat_popular_question_2_callback() {
@@ -4928,13 +5692,13 @@
4928 5692 // Render the input field
4929 5693 printf(
4930 5694 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
4931 5695 esc_attr($popular_question_2),
4932 - esc_attr__('Enter Popular Question 2', 'mxchat')
5696 + esc_attr__('Enter Quick Question 2', 'mxchat')
4933 5697 );
4934 5698
4935 5699 // Add a description for the field
4936 - echo '<p class="description">' . esc_html__('This will be the second popular question in the chatbot.', 'mxchat') . '</p>';
5700 + echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
4937 5701 }
4938 5702
4939 5703
4940 5704 public function mxchat_popular_question_3_callback() {
@@ -4947,13 +5711,13 @@
4947 5711 // Render the input field
4948 5712 printf(
4949 5713 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
4950 5714 esc_attr($popular_question_3),
4951 - esc_attr(__('Enter Popular Question 3', 'mxchat'))
5715 + esc_attr(__('Enter Quick Question 3', 'mxchat'))
4952 5716 );
4953 5717
4954 5718 // Add a description for the field
4955 - echo '<p class="description">' . esc_html__('This will be the third popular question in the chatbot.', 'mxchat') . '</p>';
5719 + echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
4956 5720 }
4957 5721
4958 5722 public function mxchat_additional_popular_questions_callback() {
4959 5723 $options = get_option('mxchat_options', []);
@@ -4974,9 +5738,9 @@
4974 5738 <button type="button" class="button mxchat-remove-question"
4975 5739 aria-label="%s">%s</button>
4976 5740 </div>',
4977 5741 esc_attr($question),
4978 - esc_attr(sprintf(__('Enter Additional Popular Question %d', 'mxchat'), $index + 4)),
5742 + esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
4979 5743 $index,
4980 5744 esc_attr(__('Remove question', 'mxchat')),
4981 5745 esc_html__('Remove', 'mxchat')
4982 5746 );
@@ -4991,9 +5755,9 @@
4991 5755 data-question-index="0" />
4992 5756 <button type="button" class="button mxchat-remove-question"
4993 5757 aria-label="%s">%s</button>
4994 5758 </div>',
4995 - esc_attr(__('Enter Additional Popular Question 4', 'mxchat')),
5759 + esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
4996 5760 esc_attr(__('Remove question', 'mxchat')),
4997 5761 esc_html__('Remove', 'mxchat')
4998 5762 );
4999 5763 }
@@ -5002,9 +5766,9 @@
5002 5766 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5003 5767 esc_attr(__('Add question', 'mxchat')),
5004 5768 esc_html__('Add Question', 'mxchat')
5005 5769 );
5006 - echo '<p class="description">' . esc_html__('Add as many popular questions as you need.', 'mxchat') . '</p>';
5770 + echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5007 5771 echo '</div>';
5008 5772 }
5009 5773
5010 5774 public function mxchat_brave_api_key_callback() {
@@ -5103,141 +5867,116 @@
5103 5867 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5104 5868 }
5105 5869
5106 5870 public function mxchat_chat_toolbar_toggle_callback() {
5107 - // Get chat toolbar toggle value with fallback
5108 - $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5109 - $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5871 + // Get chat toolbar toggle value with fallback
5872 + $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5873 + $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5110 5874
5111 - // Check if the plugin is activated (paid feature)
5112 - $disabled = $this->is_activated ? '' : 'disabled';
5113 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5875 + // Output the toggle switch
5876 + echo '<label class="toggle-switch">';
5877 + echo sprintf(
5878 + '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
5879 + esc_attr($checked)
5880 + );
5881 + echo '<span class="slider"></span>';
5882 + echo '</label>';
5114 5883
5115 - echo '<div class="' . esc_attr($class) . '">';
5884 + 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>';
5885 +}
5886 +/**
5887 + * Callback for PDF upload button toggle setting
5888 + */
5889 +public function mxchat_show_pdf_upload_button_callback() {
5890 + // Get toggle value with fallback
5891 + $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
5892 + $checked = ($show_pdf_button === 'on') ? 'checked' : '';
5116 5893
5117 - // Output the toggle switch
5118 - echo '<label class="toggle-switch">';
5119 - echo sprintf(
5120 - '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s %s />',
5121 - esc_attr($checked),
5122 - esc_attr($disabled)
5123 - );
5124 - echo '<span class="slider"></span>';
5125 - echo '</label>';
5894 + // Output the toggle switch
5895 + echo '<label class="toggle-switch">';
5896 + echo sprintf(
5897 + '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
5898 + esc_attr($checked)
5899 + );
5900 + echo '<span class="slider"></span>';
5901 + echo '</label>';
5126 5902
5127 - // Pro feature overlay
5128 - if (!$this->is_activated) {
5129 - echo '<div class="pro-feature-overlay">';
5130 - echo '<a href="https://mxchat.ai/" target="_blank">';
5131 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5132 - echo '</a>';
5133 - echo '</div>';
5134 - }
5903 + echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5904 +}
5905 +/**
5906 + * Callback for Word upload button toggle setting
5907 + */
5908 +public function mxchat_show_word_upload_button_callback() {
5909 + // Get toggle value with fallback
5910 + $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
5911 + $checked = ($show_word_button === 'on') ? 'checked' : '';
5135 5912
5136 - echo '</div>';
5137 - 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>';
5913 + // Output the toggle switch
5914 + echo '<label class="toggle-switch">';
5915 + echo sprintf(
5916 + '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
5917 + esc_attr($checked)
5918 + );
5919 + echo '<span class="slider"></span>';
5920 + echo '</label>';
5921 +
5922 + echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5138 5923 }
5139 5924
5140 -
5141 5925 public function mxchat_pdf_intent_trigger_text_callback() {
5142 - $disabled = $this->is_activated ? '' : 'disabled';
5143 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5144 5926 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5145 5927
5146 - echo '<div class="' . esc_attr($class) . '">';
5147 5928 echo sprintf(
5148 5929 '<textarea id="pdf_intent_trigger_text"
5149 5930 name="pdf_intent_trigger_text"
5150 5931 rows="3"
5151 5932 cols="50"
5152 - placeholder="%s"
5153 - %s>%s</textarea>',
5933 + placeholder="%s">%s</textarea>',
5154 5934 esc_attr__('Enter trigger text', 'mxchat'),
5155 - esc_attr($disabled),
5156 5935 isset($this->options['pdf_intent_trigger_text'])
5157 5936 ? esc_textarea($this->options['pdf_intent_trigger_text'])
5158 5937 : esc_textarea($default_text)
5159 5938 );
5160 5939 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5161 -
5162 - if (!$this->is_activated) {
5163 - echo '<div class="pro-feature-overlay">';
5164 - echo '<a href="https://mxchat.ai/" target="_blank">';
5165 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5166 - echo '</a>';
5167 - echo '</div>';
5168 - }
5169 - echo '</div>';
5170 5940 }
5171 5941
5172 5942 public function mxchat_pdf_intent_success_text_callback() {
5173 - $disabled = $this->is_activated ? '' : 'disabled';
5174 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5175 5943 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5176 5944
5177 - echo '<div class="' . esc_attr($class) . '">';
5178 5945 echo sprintf(
5179 5946 '<textarea id="pdf_intent_success_text"
5180 5947 name="pdf_intent_success_text"
5181 5948 rows="3"
5182 5949 cols="50"
5183 - placeholder="%s"
5184 - %s>%s</textarea>',
5950 + placeholder="%s">%s</textarea>',
5185 5951 esc_attr__('Enter success text', 'mxchat'),
5186 - esc_attr($disabled),
5187 5952 isset($this->options['pdf_intent_success_text'])
5188 5953 ? esc_textarea($this->options['pdf_intent_success_text'])
5189 5954 : esc_textarea($default_text)
5190 5955 );
5191 5956 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5192 -
5193 - if (!$this->is_activated) {
5194 - echo '<div class="pro-feature-overlay">';
5195 - echo '<a href="https://mxchat.ai/" target="_blank">';
5196 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5197 - echo '</a>';
5198 - echo '</div>';
5199 - }
5200 - echo '</div>';
5201 5957 }
5202 5958
5203 5959 public function mxchat_pdf_intent_error_text_callback() {
5204 - $disabled = $this->is_activated ? '' : 'disabled';
5205 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5206 5960 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5207 5961
5208 - echo '<div class="' . esc_attr($class) . '">';
5209 5962 echo sprintf(
5210 5963 '<textarea id="pdf_intent_error_text"
5211 5964 name="pdf_intent_error_text"
5212 5965 rows="3"
5213 5966 cols="50"
5214 - placeholder="%s"
5215 - %s>%s</textarea>',
5967 + placeholder="%s">%s</textarea>',
5216 5968 esc_attr__('Enter error text', 'mxchat'),
5217 - esc_attr($disabled),
5218 5969 isset($this->options['pdf_intent_error_text'])
5219 5970 ? esc_textarea($this->options['pdf_intent_error_text'])
5220 5971 : esc_textarea($default_text)
5221 5972 );
5222 5973 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5223 -
5224 - if (!$this->is_activated) {
5225 - echo '<div class="pro-feature-overlay">';
5226 - echo '<a href="https://mxchat.ai/" target="_blank">';
5227 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5228 - echo '</a>';
5229 - echo '</div>';
5230 - }
5231 - echo '</div>';
5232 5974 }
5233 5975
5234 5976 public function mxchat_pdf_max_pages_callback() {
5235 - $disabled = $this->is_activated ? '' : 'disabled';
5236 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5237 5977 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5238 5978
5239 - echo '<div class="' . esc_attr($class) . '">';
5240 5979 echo sprintf(
5241 5980 '<input type="range"
5242 5981 id="pdf_max_pages"
5243 5982 name="pdf_max_pages"
@@ -5243,37 +5982,24 @@
5243 5982 name="pdf_max_pages"
5244 5983 min="1"
5245 5984 max="69"
5246 5985 value="%d"
5247 - %s
5248 5986 class="range-slider" />',
5249 - esc_attr($max_pages),
5250 - esc_attr($disabled)
5987 + esc_attr($max_pages)
5251 5988 );
5252 5989 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5253 5990 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5254 -
5255 - if (!$this->is_activated) {
5256 - echo '<div class="pro-feature-overlay">';
5257 - echo '<a href="https://mxchat.ai/" target="_blank">';
5258 - echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
5259 - echo '</a>';
5260 - echo '</div>';
5261 - }
5262 - echo '</div>';
5263 5991 }
5264 5992
5265 5993 public function mxchat_live_agent_status_callback() {
5266 - $disabled = $this->is_activated ? '' : 'disabled';
5267 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5268 - $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off';
5994 + // Always get fresh options instead of using cached $this->options
5995 + $fresh_options = get_option('mxchat_options');
5996 + $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
5269 5997
5270 - echo '<div class="' . esc_attr($class) . '">';
5271 5998 echo '<label class="toggle-switch">';
5272 5999 echo sprintf(
5273 - '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s %s />',
5274 - checked($status, 'on', false),
5275 - esc_attr($disabled)
6000 + '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
6001 + checked($status, 'on', false)
5276 6002 );
5277 6003 echo '<span class="slider"></span>';
5278 6004 echo '</label>';
5279 6005 echo '<label for="live_agent_status" class="mxchat-status-label">';
@@ -5278,98 +6004,83 @@
5278 6004 echo '</label>';
5279 6005 echo '<label for="live_agent_status" class="mxchat-status-label">';
5280 6006 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5281 6007 echo '</label>';
5282 -
5283 - if (!$this->is_activated) {
5284 - echo '<div class="pro-feature-overlay">';
5285 - 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>';
5286 - echo '</div>';
5287 - }
5288 - echo '</div>';
5289 6008 }
5290 6009
5291 -
5292 -// Away Message Callback
5293 6010 public function mxchat_live_agent_away_message_callback() {
5294 - $disabled = $this->is_activated ? '' : 'disabled';
5295 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5296 6011 $message = isset($this->options['live_agent_away_message'])
5297 6012 ? $this->options['live_agent_away_message']
5298 6013 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5299 6014
5300 - echo '<div class="' . esc_attr($class) . '">';
5301 6015 printf(
5302 - '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50" %s>%s</textarea>',
5303 - esc_attr($disabled),
6016 + '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
5304 6017 esc_textarea($message)
5305 6018 );
5306 6019 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5307 -
5308 - if (!$this->is_activated) {
5309 - echo '<div class="pro-feature-overlay">';
5310 - 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>';
5311 - echo '</div>';
5312 - }
5313 - echo '</div>';
5314 6020 }
5315 6021
5316 6022 public function mxchat_live_agent_notification_message_callback() {
5317 - $disabled = $this->is_activated ? '' : 'disabled';
5318 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5319 6023 $message = isset($this->options['live_agent_notification_message'])
5320 6024 ? $this->options['live_agent_notification_message']
5321 6025 : __('Live agent has been notified.', 'mxchat');
5322 6026
5323 - echo '<div class="' . esc_attr($class) . '">';
5324 6027 printf(
5325 - '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50" %s>%s</textarea>',
5326 - esc_attr($disabled),
6028 + '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
5327 6029 esc_textarea($message)
5328 6030 );
5329 6031 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5330 -
5331 - if (!$this->is_activated) {
5332 - echo '<div class="pro-feature-overlay">';
5333 - 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>';
5334 - echo '</div>';
5335 - }
5336 - echo '</div>';
5337 6032 }
5338 6033
5339 6034 public function mxchat_live_agent_webhook_url_callback() {
5340 - $disabled = $this->is_activated ? '' : 'disabled';
5341 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5342 6035 $webhook_url = isset($this->options['live_agent_webhook_url'])
5343 6036 ? esc_url($this->options['live_agent_webhook_url'])
5344 6037 : esc_url(get_option('live_agent_webhook_url', ''));
5345 6038
5346 - echo '<div class="' . esc_attr($class) . '">';
5347 6039 printf(
5348 - '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" %s />',
5349 - $webhook_url,
5350 - esc_attr($disabled)
6040 + '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
6041 + $webhook_url
5351 6042 );
5352 6043 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5353 6044 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
6045 +}
5354 6046
5355 - if (!$this->is_activated) {
5356 - echo '<div class="pro-feature-overlay">';
5357 - 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>';
5358 - echo '</div>';
5359 - }
5360 - echo '</div>';
6047 +public function mxchat_live_agent_secret_key_callback() {
6048 + printf(
6049 + '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
6050 + isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
6051 + );
6052 + echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6053 + echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5361 6054 }
5362 6055
6056 +public function mxchat_live_agent_bot_token_callback() {
6057 + printf(
6058 + '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
6059 + isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
6060 + );
6061 + echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6062 + echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
6063 +}
5363 6064
6065 +public function mxchat_live_agent_user_ids_callback() {
6066 + $user_ids = isset($this->options['live_agent_user_ids'])
6067 + ? esc_textarea($this->options['live_agent_user_ids'])
6068 + : '';
6069 +
6070 + printf(
6071 + '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
6072 + $user_ids
6073 + );
6074 + echo '<p class="description">' . esc_html__('Enter Slack User IDs of agents who should be automatically invited to chat channels (one per line). Find user IDs in Slack profiles under "More" → "Copy member ID". Example: U1234567890', 'mxchat') . '</p>';
6075 +}
6076 +
5364 6077 public function mxchat_similarity_threshold_callback() {
5365 6078 // Load from mxchat_options array
5366 6079 $options = get_option('mxchat_options', []);
5367 6080
5368 - // Get value with backwards compatibility
5369 - $threshold = isset($options['similarity_threshold'])
5370 - ? $options['similarity_threshold']
5371 - : get_option('mxchat_similarity_threshold', 80);
6081 + // Get value from options array with default of 80
6082 + $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
5372 6083
5373 6084 echo '<div class="slider-container">';
5374 6085 echo sprintf(
5375 6086 '<input type="range"
@@ -5374,9 +6085,9 @@
5374 6085 echo sprintf(
5375 6086 '<input type="range"
5376 6087 id="similarity_threshold"
5377 6088 name="similarity_threshold"
5378 - min="70"
6089 + min="20"
5379 6090 max="85"
5380 6091 step="1"
5381 6092 value="%s"
5382 6093 class="range-slider" />',
@@ -5386,186 +6097,195 @@
5386 6097 '<span id="threshold_value" class="range-value">%s</span>',
5387 6098 esc_html($threshold)
5388 6099 );
5389 6100 echo '</div>';
5390 -
5391 6101 echo '<p class="description">';
5392 - 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');
6102 + echo esc_html__('Adjust similarity threshold for optimal content matching. Too high may limit knowledge retrieval. We highly recommend using the MxChat Debugger found on the left side of your website when logged in as admin while testing the bot.', 'mxchat');
5393 6103 echo '</p>';
5394 6104 }
5395 6105
6106 +public function mxchat_enqueue_admin_assets() {
6107 + // Get plugin version (define this in your main plugin file)
6108 + $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '2.3.9';
5396 6109
6110 + // Use file modification time for development (remove in production)
6111 + if (defined('WP_DEBUG') && WP_DEBUG) {
6112 + $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
6113 + }
5397 6114
5398 -public function mxchat_live_agent_secret_key_callback() {
5399 - $disabled = $this->is_activated ? '' : 'disabled';
5400 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6115 + $current_page = isset($_GET['page']) ? $_GET['page'] : '';
6116 + $plugin_url = plugin_dir_url(__FILE__) . '../';
5401 6117
5402 - echo '<div class="' . esc_attr($class) . '">';
5403 - printf(
5404 - '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" %s />',
5405 - isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '',
5406 - esc_attr($disabled)
5407 - );
5408 - echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5409 - echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
6118 + // Only load on MxChat pages
6119 + if (strpos($current_page, 'mxchat') === false) {
6120 + return;
6121 + }
5410 6122
5411 - if (!$this->is_activated) {
5412 - echo '<div class="pro-feature-overlay">';
5413 - 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>';
5414 - echo '</div>';
5415 - }
5416 - echo '</div>';
6123 + // Always load these on all MxChat pages
6124 + $this->enqueue_core_admin_assets($plugin_url, $version);
6125 + $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
6126 + $this->localize_admin_scripts($current_page);
5417 6127 }
6128 +private function enqueue_core_admin_assets($plugin_url, $version) {
6129 + // Core admin styles
6130 + wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
6131 + wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
5418 6132
5419 -public function mxchat_live_agent_bot_token_callback() {
5420 - $disabled = $this->is_activated ? '' : 'disabled';
5421 - $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
5422 -
5423 - echo '<div class="' . esc_attr($class) . '">';
5424 - printf(
5425 - '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" %s />',
5426 - isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '',
5427 - esc_attr($disabled)
5428 - );
5429 - echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5430 - echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5431 -
5432 - if (!$this->is_activated) {
5433 - echo '<div class="pro-feature-overlay">';
5434 - 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>';
5435 - echo '</div>';
5436 - }
5437 - echo '</div>';
6133 + // Core admin scripts
6134 + wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
5438 6135 }
6136 +private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
6137 + switch ($current_page) {
6138 + case 'mxchat-prompts':
6139 + // Knowledge processing page assets
6140 + wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
6141 + wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
6142 + // NEW: Add the knowledge processing script
6143 + wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
6144 + break;
5439 6145
5440 -public function mxchat_enqueue_admin_assets() {
5441 - wp_enqueue_style('wp-color-picker');
6146 + case 'mxchat-transcripts':
6147 + wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
6148 + wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
6149 + break;
5442 6150
5443 - // Get the plugin version or file modification time for cache busting
5444 - $plugin_version = '2.0.3'; // Replace this with your plugin's version
6151 + case 'mxchat-actions':
6152 + wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
6153 + break;
5445 6154
5446 - // File paths
5447 - $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js';
5448 - $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js';
5449 - $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css';
5450 - $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css';
5451 - $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css';
5452 - $transcripts_js_path = plugin_dir_path(__FILE__) . '../js/mxchat_transcripts.js';
6155 + case 'mxchat-activation':
6156 + wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
6157 + break;
6158 + default:
6159 + wp_enqueue_script(
6160 + 'mxchat-test-streaming-js',
6161 + $plugin_url . 'js/mxchat-test-streaming.js',
6162 + ['jquery'],
6163 + $version,
6164 + true
6165 + );
5453 6166
5454 - // Check if files exist and get modification times
5455 - $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version;
5456 - $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version;
5457 - $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version;
5458 - $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version;
5459 - $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version;
5460 - $transcripts_js_version = file_exists($transcripts_js_path) ? filemtime($transcripts_js_path) : $plugin_version;
5461 -
5462 - // Enqueue scripts and styles with corrected paths
5463 - wp_enqueue_script(
5464 - 'mxchat-color-picker',
5465 - plugin_dir_url(__FILE__) . '../js/my-color-picker.js',
5466 - array('wp-color-picker'),
5467 - $color_picker_version,
5468 - true
5469 - );
5470 -
5471 - wp_enqueue_script(
5472 - 'mxchat-embedding-check',
5473 - plugin_dir_url(__FILE__) . '../js/embedding-check.js',
5474 - array(),
5475 - $embedding_check_version,
5476 - true
5477 - );
5478 -
5479 - wp_enqueue_script(
5480 - 'mxchat-transcripts-js',
5481 - plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js',
5482 - array('jquery'),
5483 - $transcripts_js_version,
5484 - true
5485 - );
5486 -
5487 - wp_enqueue_script(
5488 - 'mxchat-admin-js',
5489 - plugin_dir_url(__FILE__) . '../js/mxchat-admin.js',
5490 - array('jquery'),
5491 - $plugin_version,
5492 - true
5493 - );
5494 -
5495 - wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array(
6167 + wp_localize_script('mxchat-test-streaming-js', 'mxchatTestStreamingAjax', [
6168 + 'ajax_url' => admin_url('admin-ajax.php'),
6169 + 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce')
6170 + ]);
6171 + break;
6172 + }
6173 +}
6174 +private function localize_admin_scripts($current_page) {
6175 + // Base localization data for main admin script
6176 + $base_data = array(
5496 6177 'ajax_url' => admin_url('admin-ajax.php'),
6178 + 'nonce' => wp_create_nonce('mxchat_status_nonce'),
6179 + 'admin_url' => admin_url(),
5497 6180 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5498 6181 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5499 6182 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5500 - ));
6183 + 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
6184 + 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6185 + 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
6186 + 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
6187 + 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
6188 + 'is_activated' => $this->is_activated ? '1' : '0',
6189 + 'status_refresh_interval' => 5000,
6190 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
6191 + 'ajaxurl' => admin_url('admin-ajax.php') // For jQuery fallback
6192 + );
5501 6193
5502 - // Enqueue the admin CSS
5503 - wp_enqueue_style(
5504 - 'mxchat-admin-css',
5505 - plugin_dir_url(__FILE__) . '../css/admin-style.css',
5506 - array(),
5507 - $admin_css_version
5508 - );
6194 + // Localize main admin script with base data
6195 + wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
5509 6196
5510 - wp_enqueue_style(
5511 - 'mxchat-knowledge-css',
5512 - plugin_dir_url(__FILE__) . '../css/knowledge-style.css',
5513 - array(),
5514 - $knowledge_css_version
5515 - );
6197 + // Page-specific localizations
6198 + $this->localize_page_specific_scripts($current_page);
6199 +}
6200 +private function localize_page_specific_scripts($current_page) {
6201 + switch ($current_page) {
6202 + case 'mxchat-prompts':
6203 + // Status updater localization
6204 + wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
6205 + 'ajax_url' => admin_url('admin-ajax.php'),
6206 + 'nonce' => wp_create_nonce('mxchat_status_nonce')
6207 + ));
5516 6208
6209 + // Content selector localization
6210 + wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
6211 + 'ajaxurl' => admin_url('admin-ajax.php'),
6212 + 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
6213 + 'i18n' => array(
6214 + 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
6215 + 'selectAll' => __('Select All', 'mxchat'),
6216 + 'process' => __('Process Selected', 'mxchat'),
6217 + 'cancel' => __('Cancel', 'mxchat'),
6218 + 'noResults' => __('No content found.', 'mxchat')
6219 + )
6220 + ));
5517 6221
5518 - wp_enqueue_style(
5519 - 'mxchat-intent-css',
5520 - plugin_dir_url(__FILE__) . '../css/intent-style.css',
5521 - array(),
5522 - $intent_css_version
5523 - );
6222 + // NEW: Knowledge processing script localization using mxchatAdmin
6223 + wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
6224 + 'ajax_url' => admin_url('admin-ajax.php'),
6225 + 'status_nonce' => wp_create_nonce('mxchat_status_nonce'), // Note: changed 'nonce' to 'status_nonce'
6226 + 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
6227 + 'admin_url' => admin_url(),
6228 + 'ajaxurl' => admin_url('admin-ajax.php'),
6229 + 'status_refresh_interval' => 2000
6230 + ));
6231 + break;
5524 6232
5525 - // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin
5526 - wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array(
5527 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
5528 - 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ),
5529 - ) );
6233 + case 'mxchat-activation':
6234 + wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
6235 + 'ajax_url' => admin_url('admin-ajax.php'),
6236 + 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
6237 + ));
6238 + break;
5530 6239
5531 - // Localize the script for color picker and settings
5532 - wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6240 + case 'mxchat-settings':
6241 + default:
6242 + // Color picker and settings localization
6243 + wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
6244 + 'ajax_url' => admin_url('admin-ajax.php'),
6245 + 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
6246 + 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
6247 + 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
6248 + 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
6249 + 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
6250 + 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
6251 + 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
6252 + 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
6253 + 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
6254 + 'icon_color' => $this->options['icon_color'] ?? '#fff',
6255 + 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
6256 + 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
6257 + 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
6258 + 'loops_api_key' => $this->options['loops_api_key'] ?? '',
6259 + 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
6260 + 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
6261 + 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
6262 + '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'),
6263 + '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'),
6264 + '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'),
6265 + 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
6266 + 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
6267 + 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
6268 + 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
6269 + 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
6270 + 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
6271 + 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
6272 + 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
6273 + 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
6274 + 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
6275 + 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
6276 + 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6277 + ));
6278 + break;
6279 + }
6280 +
6281 + // Additional localization that was in the original code
6282 + wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
5533 6283 'ajax_url' => admin_url('admin-ajax.php'),
5534 - 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5535 - 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5536 - 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5537 - 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5538 - 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5539 - 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5540 - 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5541 - 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5542 - 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5543 - 'icon_color' => $this->options['icon_color'] ?? '#fff',
5544 - 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5545 - 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5546 - 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5547 - 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5548 - 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5549 - 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5550 - 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5551 - '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'),
5552 - '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'),
5553 - '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'),
5554 - 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5555 - 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5556 - 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5557 - 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5558 - 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5559 - 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5560 - 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5561 - 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5562 - 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5563 - 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
6284 + 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5564 6285 ));
5565 6286 }
5566 6287
5567 -
5568 6288 public function mxchat_sanitize($input) {
5569 6289 $new_input = array();
5570 6290
5571 6291 if (isset($input['api_key'])) {
@@ -5573,9 +6293,9 @@
5573 6293 }
5574 6294
5575 6295 if (isset($input['similarity_threshold'])) {
5576 6296 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5577 - $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range
6297 + $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5578 6298 }
5579 6299
5580 6300 if (isset($input['xai_api_key'])) {
5581 6301 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
@@ -5584,12 +6304,24 @@
5584 6304 if (isset($input['claude_api_key'])) {
5585 6305 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5586 6306 }
5587 6307
6308 + if (isset($input['enable_streaming_toggle'])) {
6309 + $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
6310 + } else {
6311 + // If checkbox not checked, it won't be in $input, so set to 'off'
6312 + $new_input['enable_streaming_toggle'] = 'off';
6313 + }
6314 +
6315 +
5588 6316 if (isset($input['deepseek_api_key'])) {
5589 6317 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5590 6318 }
5591 6319
6320 + if (isset($input['gemini_api_key'])) {
6321 + $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
6322 + }
6323 +
5592 6324 if (isset($input['enable_woocommerce_integration'])) {
5593 6325 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5594 6326 }
5595 6327
@@ -5622,12 +6354,20 @@
5622 6354 if (isset($input['append_to_body'])) {
5623 6355 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5624 6356 }
5625 6357
6358 + if (isset($input['contextual_awareness_toggle'])) {
6359 + $new_input['contextual_awareness_toggle'] = $input['contextual_awareness_toggle'] === 'on' ? 'on' : 'off';
6360 +}
6361 +
5626 6362 if (isset($input['top_bar_title'])) {
5627 6363 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5628 6364 }
5629 6365
6366 + if (isset($input['ai_agent_text'])) {
6367 + $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
6368 + }
6369 +
5630 6370 if (isset($input['enable_email_block'])) {
5631 6371 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5632 6372 }
5633 6373
@@ -5637,11 +6377,20 @@
5637 6377 }
5638 6378 if (isset($input['email_blocker_button_text'])) {
5639 6379 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5640 6380 }
5641 -
6381 + // NEW: Sanitize name field toggle
6382 + if (isset($input['enable_name_field'])) {
6383 + $new_input['enable_name_field'] = ($input['enable_name_field'] === 'on') ? 'on' : 'off';
6384 + } else {
6385 + $new_input['enable_name_field'] = 'off';
6386 + }
6387 + // NEW: Sanitize name field placeholder
6388 + if (isset($input['name_field_placeholder'])) {
6389 + $new_input['name_field_placeholder'] = sanitize_text_field($input['name_field_placeholder']);
6390 + }
5642 6391 if (isset($input['intro_message'])) {
5643 - $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field
6392 + $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5644 6393 }
5645 6394
5646 6395 if (isset($input['input_copy'])) {
5647 6396 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
@@ -5650,58 +6399,108 @@
5650 6399 if (isset($input['rate_limit_message'])) {
5651 6400 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5652 6401 }
5653 6402
5654 - if (isset($input['rate_limit_logged_out'])) {
5655 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values
5656 - $rate_limit = sanitize_text_field($input['rate_limit_logged_out']);
6403 +// Handle the new rate limits format
6404 +if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
6405 + $new_input['rate_limits'] = array();
6406 + $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
6407 + $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
5657 6408
5658 - if (in_array($rate_limit, $allowed_limits, true)) {
5659 - $new_input['rate_limit_logged_out'] = $rate_limit;
5660 - } else {
5661 - $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users
6409 + foreach ($input['rate_limits'] as $role_id => $settings) {
6410 + $new_input['rate_limits'][$role_id] = array();
6411 +
6412 + // Sanitize limit
6413 + if (isset($settings['limit'])) {
6414 + $limit = sanitize_text_field($settings['limit']);
6415 + if (in_array($limit, $allowed_limits, true)) {
6416 + $new_input['rate_limits'][$role_id]['limit'] = $limit;
6417 + } else {
6418 + $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
6419 + }
5662 6420 }
5663 - }
5664 6421
5665 - // Handle role rate limits
5666 - if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) {
5667 - $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited');
5668 - $new_input['role_rate_limits'] = array();
5669 -
5670 - foreach ($input['role_rate_limits'] as $role_id => $rate_limit) {
5671 - $rate_limit = sanitize_text_field($rate_limit);
5672 - if (in_array($rate_limit, $allowed_limits, true)) {
5673 - $new_input['role_rate_limits'][$role_id] = $rate_limit;
6422 + // Sanitize timeframe
6423 + if (isset($settings['timeframe'])) {
6424 + $timeframe = sanitize_text_field($settings['timeframe']);
6425 + if (in_array($timeframe, $allowed_timeframes, true)) {
6426 + $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
5674 6427 } else {
5675 - $new_input['role_rate_limits'][$role_id] = '100'; // Default
6428 + $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
5676 6429 }
5677 6430 }
6431 +
6432 + // Sanitize message
6433 + if (isset($settings['message'])) {
6434 + $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
6435 + }
5678 6436 }
6437 +}
5679 6438
5680 6439 if (isset($input['pre_chat_message'])) {
5681 6440 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5682 6441 }
5683 6442
5684 - if (isset($input['model'])) {
6443 + if (isset($input['voyage_api_key'])) {
6444 + $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
6445 + }
6446 +
6447 + // Add to your sanitize function
6448 + if (isset($input['embedding_model'])) {
5685 6449 $allowed_models = array(
5686 - 'grok-beta',
5687 - 'grok-2',
5688 - 'deepseek-chat',
5689 - 'claude-3-5-sonnet-20241022',
5690 - 'claude-3-opus-20240229',
5691 - 'claude-3-sonnet-20240229',
5692 - 'claude-3-haiku-20240307',
5693 - 'gpt-4o',
5694 - 'gpt-4o-mini',
5695 - 'gpt-4-turbo',
5696 - 'gpt-4',
5697 - 'gpt-3.5-turbo',
6450 + 'text-embedding-ada-002',
6451 + 'text-embedding-3-small',
6452 + 'text-embedding-3-large',
6453 + 'voyage-3-large',
6454 + 'gemini-embedding-exp-03-07'
5698 6455 );
5699 - if (in_array($input['model'], $allowed_models)) {
5700 - $new_input['model'] = sanitize_text_field($input['model']);
6456 + if (in_array($input['embedding_model'], $allowed_models)) {
6457 + $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5701 6458 }
5702 6459 }
5703 6460
6461 +if (isset($input['model'])) {
6462 + $allowed_models = array(
6463 + 'gemini-2.0-flash',
6464 + 'gemini-2.0-flash-lite',
6465 + 'gemini-1.5-pro',
6466 + 'gemini-1.5-flash',
6467 +
6468 + 'grok-4-0709', // New model: Grok 4 added here
6469 + 'grok-3-beta',
6470 + 'grok-3-fast-beta',
6471 + 'grok-3-mini-beta',
6472 + 'grok-3-mini-fast-beta',
6473 + 'grok-2',
6474 +
6475 + 'deepseek-chat',
6476 +
6477 + 'claude-opus-4-20250514',
6478 + 'claude-sonnet-4-20250514',
6479 + 'claude-3-7-sonnet-20250219',
6480 + 'claude-3-5-sonnet-20241022',
6481 + 'claude-3-opus-20240229',
6482 + 'claude-3-sonnet-20240229',
6483 + 'claude-3-haiku-20240307',
6484 +
6485 + // NEW: GPT-5 family
6486 + 'gpt-5',
6487 + 'gpt-5-mini',
6488 + 'gpt-5-nano',
6489 +
6490 + 'gpt-4o',
6491 + 'gpt-4.1-2025-04-14',
6492 + 'gpt-4o-mini',
6493 + 'gpt-4-turbo',
6494 + 'gpt-4',
6495 + 'gpt-3.5-turbo',
6496 + );
6497 + if (in_array($input['model'], $allowed_models)) {
6498 + $new_input['model'] = sanitize_text_field($input['model']);
6499 + }
6500 +}
6501 +
6502 +
5704 6503 if (isset($input['close_button_color'])) {
5705 6504 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5706 6505 }
5707 6506
@@ -5756,8 +6555,12 @@
5756 6555 if (isset($input['top_bar_bg_color'])) {
5757 6556 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5758 6557 }
5759 6558
6559 + if (isset($input['quick_questions_toggle_color'])) {
6560 + $new_input['quick_questions_toggle_color'] = sanitize_hex_color($input['quick_questions_toggle_color']);
6561 + }
6562 +
5760 6563 if (isset($input['send_button_font_color'])) {
5761 6564 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5762 6565 }
5763 6566
@@ -5815,15 +6618,14 @@
5815 6618 if (isset($input['loops_mailing_list'])) {
5816 6619 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5817 6620 }
5818 6621
5819 - // Sanitize Triggered Phrase Response
6622 +// Sanitize Triggered Phrase Response
5820 6623 if (isset($input['triggered_phrase_response'])) {
5821 6624 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5822 6625 }
5823 -
5824 6626 if (isset($input['email_capture_response'])) {
5825 - $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
6627 + $new_input['email_capture_response'] = wp_kses_post($input['email_capture_response']);
5826 6628 }
5827 6629
5828 6630 // Sanitize Brave Search Settings
5829 6631 if (isset($input['brave_api_key'])) {
@@ -5856,8 +6658,22 @@
5856 6658 if (isset($input['chat_toolbar_toggle'])) {
5857 6659 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
5858 6660 }
5859 6661
6662 + // Sanitize PDF upload button toggle
6663 + if (isset($input['show_pdf_upload_button'])) {
6664 + $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
6665 + } else {
6666 + $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
6667 + }
6668 +
6669 + // Sanitize Word upload button toggle
6670 + if (isset($input['show_word_upload_button'])) {
6671 + $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
6672 + } else {
6673 + $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
6674 + }
6675 +
5860 6676 if (isset($input['pdf_intent_trigger_text'])) {
5861 6677 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
5862 6678 }
5863 6679
@@ -5887,8 +6703,12 @@
5887 6703 if (isset($input['live_agent_bot_token'])) {
5888 6704 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
5889 6705 }
5890 6706
6707 + if (isset($input['live_agent_user_ids'])) {
6708 + $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
6709 + }
6710 +
5891 6711 if (isset($input['live_agent_status'])) {
5892 6712 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
5893 6713 }
5894 6714 if (isset($input['live_agent_away_message'])) {
@@ -5911,62 +6731,10 @@
5911 6731 }
5912 6732
5913 6733
5914 6734
5915 -private static function mxchat_extract_main_content($html) {
5916 - if (empty($html)) {
5917 - //error_log('mxchat_extract_main_content: Empty HTML content received');
5918 - return '';
5919 - }
5920 6735
5921 - try {
5922 - $dom = new DOMDocument;
5923 - libxml_use_internal_errors(true); // Suppress HTML parsing errors
5924 6736
5925 - // Simple load of HTML, with @ to suppress warnings
5926 - @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
5927 -
5928 - $xpath = new DOMXPath($dom);
5929 -
5930 - // Simplified selectors focusing on common content areas
5931 - $selectors = [
5932 - '//article',
5933 - '//*[@id="content"]',
5934 - '//*[@class="entry-content"]',
5935 - '//main'
5936 - ];
5937 -
5938 - foreach ($selectors as $selector) {
5939 - $nodes = $xpath->query($selector);
5940 - if ($nodes && $nodes->length > 0) {
5941 - $content = '';
5942 - foreach ($nodes as $node) {
5943 - $content .= $dom->saveHTML($node);
5944 - }
5945 - if (!empty($content)) {
5946 - return $content;
5947 - }
5948 - }
5949 - }
5950 -
5951 - // Fallback: Return the entire body content if no specific selector matches
5952 - $body = $dom->getElementsByTagName('body');
5953 - if ($body->length > 0) {
5954 - return $dom->saveHTML($body->item(0));
5955 - }
5956 -
5957 - // Last resort: return the original HTML
5958 - return $html;
5959 -
5960 - } catch (Exception $e) {
5961 - //error_log('mxchat_extract_main_content error: ' . $e->getMessage());
5962 - return $html; // Return original HTML if parsing fails
5963 - } finally {
5964 - libxml_clear_errors();
5965 - }
5966 -}
5967 -
5968 -
5969 6737 private function mxchat_fetch_loops_mailing_lists($api_key) {
5970 6738 $url = 'https://app.loops.so/api/v1/lists';
5971 6739 $response = wp_remote_get($url, array(
5972 6740 'headers' => array(
@@ -5984,12 +6752,8 @@
5984 6752
5985 6753 return isset($lists) && is_array($lists) ? $lists : array();
5986 6754 }
5987 6755
5988 -
5989 -
5990 -
5991 -
5992 6756 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
5993 6757 if (empty($vec1) || empty($vec2)) {
5994 6758 return 0.0;
5995 6759 }
@@ -6010,8 +6774,602 @@
6010 6774 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
6011 6775 }
6012 6776 }
6013 6777
6778 +/**
6779 + * Validates nonce and deletes all chat prompts
6780 + */
6781 +public function mxchat_handle_delete_all_prompts() {
6782 + // Verify nonce
6783 + if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
6784 + wp_die(__('Nonce verification failed.', 'mxchat'));
6785 + }
6786 + // Check permissions
6787 + if (!current_user_can('manage_options')) {
6788 + wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
6789 + }
6790 + $success = true;
6791 + $error_messages = array();
6792 + // Check if Pinecone is enabled and configured
6793 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6794 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6795 + if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
6796 + //error_log('[MXCHAT-DELETE] Pinecone is enabled - deleting all from Pinecone');
6797 + // Get Pinecone configuration
6798 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6799 + $pinecone_manager = new MxChat_Pinecone_Manager();
6800 + $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
6801 + if (!$result['success']) {
6802 + $success = false;
6803 + $error_messages[] = $result['message'];
6804 + }
6805 + // Clear Pinecone vector cache
6806 + delete_option('mxchat_pinecone_vector_ids_cache');
6807 + // Clear processed content caches
6808 + delete_option('mxchat_pinecone_processed_cache');
6809 + delete_option('mxchat_processed_content_cache');
6810 +
6811 + // CLEAR THE PINECONE RECORDS CACHE (NEW LINE)
6812 + delete_transient('mxchat_pinecone_recent_1k_cache');
6813 +
6814 + } else {
6815 + //error_log('[MXCHAT-DELETE] Pinecone not enabled - deleting from WordPress database');
6816 + // Delete from WordPress database
6817 + global $wpdb;
6818 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6819 + $result = $wpdb->query("DELETE FROM {$table_name}");
6820 + if ($result === false) {
6821 + $success = false;
6822 + $error_messages[] = 'Failed to delete from WordPress database';
6823 + }
6824 + }
6825 + // Clear relevant cache
6826 + wp_cache_delete('all_prompts', 'mxchat_prompts');
6827 + // Set appropriate admin notice
6828 + if ($success) {
6829 + set_transient('mxchat_admin_notice_success',
6830 + __('All prompts deleted successfully.', 'mxchat'), 30);
6831 + } else {
6832 + set_transient('mxchat_admin_notice_error',
6833 + __('Failed to delete all prompts: ', 'mxchat') . implode(', ', $error_messages), 30);
6834 + }
6835 + // Redirect back with a success message
6836 + $redirect_url = add_query_arg(array(
6837 + 'page' => 'mxchat-prompts',
6838 + 'all_deleted' => $success ? 'true' : 'false'
6839 + ), admin_url('admin.php'));
6840 + wp_safe_redirect($redirect_url);
6841 + exit;
6842 +}
6843 +
6844 + /**
6845 + * Handles deletion prompt with nonce validation
6846 + */
6847 + public function mxchat_handle_delete_prompt() {
6848 + // Sanitize and validate nonce
6849 + $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
6850 + if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
6851 + wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
6852 + }
6853 +
6854 + // Check permissions
6855 + if (!current_user_can('manage_options')) {
6856 + wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
6857 + }
6858 +
6859 + // Get ID and source parameters
6860 + $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
6861 + $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
6862 +
6863 + if (empty($id)) {
6864 + wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
6865 + }
6866 +
6867 + $success = false;
6868 + $error_message = '';
6869 +
6870 + // Check if Pinecone is enabled and determine source automatically if not specified
6871 + $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6872 + $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6873 +
6874 + // If source is not specified, determine based on Pinecone configuration
6875 + if (empty($source)) {
6876 + $source = $use_pinecone ? 'pinecone' : 'wordpress';
6877 + }
6878 +
6879 + if ($source === 'pinecone' || $use_pinecone) {
6880 + //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
6881 +
6882 + // Handle Pinecone deletion
6883 + if (empty($pinecone_options['mxchat_pinecone_host']) ||
6884 + empty($pinecone_options['mxchat_pinecone_api_key'])) {
6885 + wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
6886 + }
6887 +
6888 + // Delete from Pinecone using the vector ID directly
6889 + $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
6890 + $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
6891 + $id,
6892 + $pinecone_options['mxchat_pinecone_api_key'],
6893 + $pinecone_options['mxchat_pinecone_host']
6894 + );
6895 + if ($result['success']) {
6896 + $success = true;
6897 +
6898 + // Remove from vector cache
6899 + $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
6900 + $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
6901 +
6902 + set_transient('mxchat_admin_notice_success',
6903 + esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
6904 + } else {
6905 + $error_message = $result['message'];
6906 + set_transient('mxchat_admin_notice_error',
6907 + esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
6908 + }
6909 + } else {
6910 + //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
6911 +
6912 + // Handle WordPress database deletion
6913 + global $wpdb;
6914 + $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6915 +
6916 + // Clear cache and delete prompt
6917 + wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
6918 +
6919 + $result = $wpdb->delete(
6920 + $table_name,
6921 + array('id' => intval($id)),
6922 + array('%d')
6923 + );
6924 +
6925 + if ($result !== false) {
6926 + $success = true;
6927 + set_transient('mxchat_admin_notice_success',
6928 + esc_html__('Entry deleted successfully.', 'mxchat'), 30);
6929 + } else {
6930 + set_transient('mxchat_admin_notice_error',
6931 + esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
6932 + }
6933 + }
6934 +
6935 + // Redirect back to the prompts page
6936 + wp_safe_redirect(add_query_arg(
6937 + array(
6938 + 'page' => 'mxchat-prompts',
6939 + 'deleted' => $success ? 'true' : 'false'
6940 + ),
6941 + admin_url('admin.php')
6942 + ));
6943 + exit;
6944 + }
6945 +
6946 + /**
6947 + * Validates user permissions for editing chat settings
6948 + */
6949 + public function mxchat_handle_edit_intent() {
6950 + // Security checks (nonce and permissions)
6951 + if (!current_user_can('manage_options')) {
6952 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
6953 + }
6954 + check_admin_referer('mxchat_edit_intent');
6955 +
6956 + // Get POST data
6957 + $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
6958 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
6959 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
6960 + $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
6961 + $similarity_threshold = min(95, max(10, $threshold_percentage)) / 100; // Convert to 0.10–0.95
6962 +
6963 + // Validate inputs
6964 + if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
6965 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
6966 + return;
6967 + }
6968 +
6969 + $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
6970 + if (empty($phrases_array)) {
6971 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
6972 + return;
6973 + }
6974 +
6975 + // Generate embeddings with improved error handling
6976 + $vectors = [];
6977 + $failed_phrases = [];
6978 +
6979 + foreach ($phrases_array as $phrase) {
6980 + $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
6981 + if (is_array($embedding_vector)) {
6982 + $vectors[] = $embedding_vector;
6983 + } else {
6984 + $failed_phrases[] = $phrase;
6985 + }
6986 + }
6987 +
6988 + if (!empty($failed_phrases)) {
6989 + $this->handle_embedding_error(
6990 + sprintf(
6991 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
6992 + implode(', ', $failed_phrases)
6993 + )
6994 + );
6995 + return;
6996 + }
6997 +
6998 + if (empty($vectors)) {
6999 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
7000 + return;
7001 + }
7002 +
7003 + $combined_vector = $this->mxchat_average_vectors($vectors);
7004 + $serialized_vector = maybe_serialize($combined_vector);
7005 +
7006 + // Update the database
7007 + global $wpdb;
7008 + $table_name = $wpdb->prefix . 'mxchat_intents';
7009 +
7010 + $result = $wpdb->update(
7011 + $table_name,
7012 + array(
7013 + 'intent_label' => $intent_label,
7014 + 'phrases' => implode(', ', $phrases_array),
7015 + 'embedding_vector' => $serialized_vector,
7016 + 'similarity_threshold' => $similarity_threshold
7017 + ),
7018 + array('id' => $intent_id),
7019 + array('%s', '%s', '%s', '%f'), // Format: string, string, string, float
7020 + array('%d') // Where format: integer
7021 + );
7022 +
7023 + if (false === $result) {
7024 + $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
7025 + return;
7026 + }
7027 +
7028 + // Set success message and redirect
7029 + set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
7030 +
7031 + $redirect_url = add_query_arg(
7032 + array(
7033 + 'page' => 'mxchat-actions'
7034 + ),
7035 + admin_url('admin.php')
7036 + );
7037 + wp_safe_redirect($redirect_url);
7038 + exit;
7039 + }
7040 +
7041 +
7042 +/**
7043 + * Checks user permissions for managing options
7044 + */
7045 +public function mxchat_handle_add_intent() {
7046 + if (!current_user_can('manage_options')) {
7047 + wp_die(esc_html__('Unauthorized user', 'mxchat'));
7048 + }
7049 + check_admin_referer('mxchat_add_intent_nonce');
7050 +
7051 + global $wpdb;
7052 + $table_name = $wpdb->prefix . 'mxchat_intents';
7053 +
7054 + // Sanitize and get form data
7055 + $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
7056 + $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
7057 + $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
7058 +
7059 + // Get similarity threshold from form (convert percentage to decimal)
7060 + $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
7061 +
7062 + // Validate required fields
7063 + if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
7064 + $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
7065 + return;
7066 + }
7067 +
7068 + // Validate callback function
7069 + $available_callbacks = $this->mxchat_get_available_callbacks();
7070 + if (!array_key_exists($callback_function, $available_callbacks)) {
7071 + $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
7072 + return;
7073 + }
7074 +
7075 + // Check Pro requirements
7076 + $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
7077 + if ($is_pro_only && !$this->is_activated) {
7078 + $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
7079 + return;
7080 + }
7081 +
7082 + // Process phrases
7083 + $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
7084 + if (empty($phrases_array)) {
7085 + $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
7086 + return;
7087 + }
7088 +
7089 + // Generate embeddings with improved error handling
7090 + $vectors = [];
7091 + $failed_phrases = [];
7092 + foreach ($phrases_array as $phrase) {
7093 + $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
7094 + if (is_array($embedding_vector)) {
7095 + $vectors[] = $embedding_vector;
7096 + } else {
7097 + $failed_phrases[] = $phrase;
7098 + }
7099 + }
7100 +
7101 + // Check for embedding failures
7102 + if (!empty($failed_phrases)) {
7103 + $this->handle_embedding_error(
7104 + sprintf(
7105 + __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
7106 + implode(', ', $failed_phrases)
7107 + )
7108 + );
7109 + return;
7110 + }
7111 +
7112 + if (empty($vectors)) {
7113 + $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
7114 + return;
7115 + }
7116 +
7117 + // Create combined vector and insert into database
7118 + $combined_vector = $this->mxchat_average_vectors($vectors);
7119 + $serialized_vector = maybe_serialize($combined_vector);
7120 +
7121 + $result = $wpdb->insert($table_name, [
7122 + 'intent_label' => $intent_label,
7123 + 'phrases' => implode(', ', $phrases_array),
7124 + 'embedding_vector' => $serialized_vector,
7125 + 'callback_function' => $callback_function,
7126 + 'similarity_threshold' => $similarity_threshold, // Now uses the actual form value
7127 + ]);
7128 +
7129 + if ($result === false) {
7130 + $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
7131 + return;
7132 + }
7133 +
7134 + // Set success message and redirect
7135 + set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
7136 + wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
7137 + exit;
7138 +}
7139 +
7140 + /**
7141 + * Averages multiple vectors into a single vector
7142 + */
7143 + private function mxchat_average_vectors($vectors) {
7144 + $vector_length = count($vectors[0]);
7145 + $sum_vector = array_fill(0, $vector_length, 0);
7146 +
7147 + foreach ($vectors as $vector) {
7148 + for ($i = 0; $i < $vector_length; $i++) {
7149 + $sum_vector[$i] += $vector[$i];
7150 + }
7151 + }
7152 +
7153 + // Divide each component by the number of vectors to get the average
7154 + $num_vectors = count($vectors);
7155 + for ($i = 0; $i < $vector_length; $i++) {
7156 + $sum_vector[$i] /= $num_vectors;
7157 + }
7158 +
7159 + return $sum_vector;
7160 + }
7161 +
7162 +
7163 +
7164 +
7165 + /**
7166 + * Generates embeddings from input text for MXChat
7167 + */
7168 + public function mxchat_generate_embedding($text) {
7169 + // Enable detailed logging for debugging
7170 + //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
7171 + //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
7172 +
7173 + $options = get_option('mxchat_options');
7174 + $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
7175 + //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
7176 +
7177 + // Determine provider and endpoint
7178 + if (strpos($selected_model, 'voyage') === 0) {
7179 + $api_key = $options['voyage_api_key'] ?? '';
7180 + $endpoint = 'https://api.voyageai.com/v1/embeddings';
7181 + $provider_name = 'Voyage AI';
7182 + //error_log('[MXCHAT-EMBED] Using Voyage AI API');
7183 + } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
7184 + $api_key = $options['gemini_api_key'] ?? '';
7185 + $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
7186 + $provider_name = 'Google Gemini';
7187 + //error_log('[MXCHAT-EMBED] Using Google Gemini API');
7188 + } else {
7189 + $api_key = $options['api_key'] ?? '';
7190 + $endpoint = 'https://api.openai.com/v1/embeddings';
7191 + $provider_name = 'OpenAI';
7192 + //error_log('[MXCHAT-EMBED] Using OpenAI API');
7193 + }
7194 +
7195 + //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
7196 +
7197 + if (empty($api_key)) {
7198 + $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
7199 + //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
7200 + return $error_message;
7201 + }
7202 +
7203 + // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
7204 + $estimated_tokens = ceil(str_word_count($text) / 0.75);
7205 + //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
7206 +
7207 + if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
7208 + //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
7209 + // Consider truncating text here
7210 + }
7211 +
7212 + // Prepare request body based on provider
7213 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7214 + // Gemini API format
7215 + $request_body = array(
7216 + 'model' => 'models/' . $selected_model,
7217 + 'content' => array(
7218 + 'parts' => array(
7219 + array('text' => $text)
7220 + )
7221 + )
7222 + );
7223 +
7224 + // Set output dimensionality to 1536 for consistency with other models
7225 + $request_body['outputDimensionality'] = 1536;
7226 + } else {
7227 + // OpenAI/Voyage API format
7228 + $request_body = array(
7229 + 'model' => $selected_model,
7230 + 'input' => $text
7231 + );
7232 +
7233 + // Add output_dimension for voyage-3-large model
7234 + if ($selected_model === 'voyage-3-large') {
7235 + $request_body['output_dimension'] = 2048;
7236 + }
7237 + }
7238 +
7239 + //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
7240 +
7241 + // Prepare headers based on provider
7242 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7243 + // Gemini uses API key as query parameter
7244 + $endpoint .= '?key=' . $api_key;
7245 + $headers = array(
7246 + 'Content-Type' => 'application/json'
7247 + );
7248 + } else {
7249 + // OpenAI/Voyage use Bearer token
7250 + $headers = array(
7251 + 'Authorization' => 'Bearer ' . $api_key,
7252 + 'Content-Type' => 'application/json'
7253 + );
7254 + }
7255 +
7256 + // Make API request
7257 + //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
7258 + $response = wp_remote_post($endpoint, array(
7259 + 'body' => wp_json_encode($request_body),
7260 + 'headers' => $headers,
7261 + 'timeout' => 60 // Increased timeout for large inputs
7262 + ));
7263 +
7264 + // Handle wp_remote_post errors
7265 + if (is_wp_error($response)) {
7266 + $error_message = $response->get_error_message();
7267 + //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
7268 + return 'Connection error: ' . $error_message;
7269 + }
7270 +
7271 + // Get and check HTTP response code
7272 + $http_code = wp_remote_retrieve_response_code($response);
7273 + //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
7274 +
7275 + if ($http_code !== 200) {
7276 + $error_body = wp_remote_retrieve_body($response);
7277 + //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
7278 +
7279 + // Try to parse error for more details
7280 + $error_json = json_decode($error_body, true);
7281 + if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
7282 + $error_type = $error_json['error']['type'] ?? 'unknown';
7283 + $error_message = $error_json['error']['message'] ?? 'No message';
7284 + //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
7285 + //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
7286 +
7287 + // Customize error message for common API errors
7288 + if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
7289 + $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
7290 + } elseif ($error_type === 'authentication_error') {
7291 + $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
7292 + }
7293 +
7294 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7295 + return $error_message;
7296 + }
7297 +
7298 + $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
7299 + //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
7300 + return $error_message;
7301 + }
7302 +
7303 + // Parse response body
7304 + $response_body = wp_remote_retrieve_body($response);
7305 + //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
7306 +
7307 + $response_data = json_decode($response_body, true);
7308 +
7309 + if (json_last_error() !== JSON_ERROR_NONE) {
7310 + $error = json_last_error_msg();
7311 + //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
7312 + //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
7313 + return "Failed to parse API response: $error";
7314 + }
7315 +
7316 + // Handle different response formats based on provider
7317 + if (strpos($selected_model, 'gemini-embedding') === 0) {
7318 + // Gemini API response format
7319 + if (isset($response_data['embedding']['values'])) {
7320 + $embedding_dimensions = count($response_data['embedding']['values']);
7321 + //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
7322 +
7323 + // Check if embedding dimensions are as expected (should be 1536)
7324 + if ($embedding_dimensions !== 1536) {
7325 + //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
7326 + }
7327 +
7328 + return $response_data['embedding']['values'];
7329 + } else {
7330 + //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
7331 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7332 +
7333 + if (isset($response_data['error'])) {
7334 + $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
7335 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7336 + return $error_message;
7337 + }
7338 +
7339 + $error_message = "Invalid Gemini API response format: No embedding found";
7340 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7341 + return $error_message;
7342 + }
7343 + } else {
7344 + // OpenAI/Voyage API response format
7345 + if (isset($response_data['data'][0]['embedding'])) {
7346 + $embedding_dimensions = count($response_data['data'][0]['embedding']);
7347 + //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
7348 +
7349 + // Check if embedding dimensions are as expected
7350 + if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
7351 + ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
7352 + //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
7353 + }
7354 +
7355 + return $response_data['data'][0]['embedding'];
7356 + } else {
7357 + //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
7358 + //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
7359 +
7360 + if (isset($response_data['error'])) {
7361 + $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
7362 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7363 + return $error_message;
7364 + }
7365 +
7366 + $error_message = "Invalid API response format: No embedding found";
7367 + //error_log('[MXCHAT-EMBED] ' . $error_message);
7368 + return $error_message;
7369 + }
7370 + }
7371 + }
6014 7372
6015 7373
6016 7374
6017 7375 }