PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.9
MxChat – AI Chatbot & Content Generation for WordPress v2.5.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
mxchat-basic / includes / class-mxchat-admin.php

class-mxchat-admin.php in MxChat – AI Chatbot & Content Generation for WordPress 2.5.9, at includes/class-mxchat-admin.php

8,438 lines 402.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Admin {
7 private $options;
8 private $chat_count;
9 private $is_activated;
10 private $knowledge_manager;
11
12 public function __construct($knowledge_manager = null) {
13 $this->options = get_option('mxchat_options');
14 $this->chat_count = get_option('mxchat_chat_count', 0);
15 $this->is_activated = $this->is_license_active();
16 $this->knowledge_manager = $knowledge_manager;
17
18 // Initialize default options if they are not set
19 if (!$this->options) {
20 $this->initialize_default_options();
21 }
22
23 // Add admin menu and initialize settings
24 add_action('admin_menu', array($this, 'mxchat_add_plugin_page'));
25 add_action('admin_init', array($this, 'mxchat_page_init'));
26 add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
27 add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
28 add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
29 add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
30 add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
31 add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
32 add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
33 add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
34 add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
35 add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
36 add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
37 add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
38 add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
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'));
43 add_action('mxchat_cleanup_old_transcripts', array($this, 'cleanup_old_transcripts'));
44
45 add_action('admin_init', array($this, 'register_pinecone_settings'));
46
47 add_action('admin_notices', array($this, 'display_admin_notices'));
48
49 add_action('wp_ajax_mxchat_test_streaming_actual', [$this, 'mxchat_handle_test_streaming_actual']);
50 add_action('wp_ajax_mxchat_test_streaming', [$this, 'mxchat_handle_test_streaming']); // Keep existing as fallback
51
52 add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
53 add_action('wp_ajax_mxchat_fetch_openrouter_models', array($this, 'fetch_openrouter_models'));
54 }
55
56 private function is_license_active() {
57 // Get the raw value without translation
58 $license_status = get_option('mxchat_license_status', 'inactive');
59
60 // Check against multiple possible values, bypassing translation issues
61 return ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
62 }
63
64 private function initialize_default_options() {
65 $default_options = array(
66 'api_key' => '',
67 'xai_api_key' => '',
68 'claude_api_key' => '',
69 'deepseek_api_key' => '',
70 'voyage_api_key' => '',
71 'gemini_api_key' => '',
72 'enable_streaming_toggle' => 'on',
73 'embedding_model' => 'text-embedding-ada-002',
74 '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:
75
76 # Response Style - CRITICALLY IMPORTANT
77 - MAXIMUM LENGTH: 1-3 short sentences per response
78 - Ultra-concise: Get straight to the answer with no filler
79 - No introductions like "Sure!" or "I\'d be happy to help"
80 - No phrases like "based on my knowledge" or "according to information"
81 - No explanatory text before giving the answer
82 - No summaries or repetition
83 - Hyperlink all URLs
84 - Respond in user\'s language
85 - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
86
87 # Knowledge Base Requirements - PREVENT HALLUCINATIONS
88 - ONLY answer questions using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
89 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
90 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
91 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
92 - Better to admit insufficient information than provide inaccurate answers',
93 'model' => esc_html__('gpt-4o', 'mxchat'),
94 'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
95 'role_rate_limits' => array(),
96 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
97 'enable_email_block' => '',
98 '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'),
99 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
100 'enable_name_field' => 'off', // NEW
101 'name_field_placeholder' => esc_html__('Enter your name', 'mxchat'), // NEW
102 'top_bar_title' => esc_html__('MxChat', 'mxchat'),
103 'intro_message' => __('Hello! How can I assist you today?', 'mxchat'),
104 'ai_agent_text' => esc_html__('AI Agent', 'mxchat'),
105 'input_copy' => esc_html__('How can I assist?', 'mxchat'),
106 'append_to_body' => esc_html__('off', 'mxchat'),
107 'contextual_awareness_toggle' => 'off',
108 'close_button_color' => esc_html__('#fff', 'mxchat'),
109 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
110 'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
111 'user_message_font_color' => esc_html__('#212121', 'mxchat'),
112 'bot_message_bg_color' => esc_html__('#212121', 'mxchat'),
113 'bot_message_font_color' => esc_html__('#fff', 'mxchat'),
114 'top_bar_bg_color' => esc_html__('#212121', 'mxchat'),
115 'send_button_font_color' => esc_html__('#212121', 'mxchat'),
116 'chat_input_font_color' => esc_html__('#212121', 'mxchat'),
117 'chatbot_background_color' => esc_html__('#212121', 'mxchat'),
118 'icon_color' => esc_html__('#fff', 'mxchat'),
119 'enable_woocommerce_integration' => esc_html__('0', 'mxchat'),
120 'link_target_toggle' => esc_html__('off', 'mxchat'),
121 'pre_chat_message' => esc_html__('Hey there! Ask me anything!', 'mxchat'),
122
123 // New fields for Loops Integration
124 'loops_api_key' => '',
125 'loops_mailing_list' => '',
126 'triggered_phrase_response' => __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
127 'email_capture_response' => __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
128 'popular_question_1' => '',
129 'popular_question_2' => '',
130 'popular_question_3' => '',
131 'pdf_intent_trigger_text' => __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
132 'pdf_intent_success_text' => __("I've processed the PDF. What questions do you have about it?", 'mxchat'),
133 'pdf_intent_error_text' => __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
134 'pdf_max_pages' => 69,
135 'show_pdf_upload_button' => 'on',
136 'show_word_upload_button' => 'on',
137
138 // Live Agent Integration
139 'live_agent_webhook_url' => '',
140 'live_agent_secret_key' => '',
141 'live_agent_bot_token' => '',
142 'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'),
143 'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'),
144 'chat_toolbar_toggle' => esc_html__('off', 'mxchat'),
145 'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'),
146 'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'),
147 'toolbar_icon_color' => esc_html__('#212121', 'mxchat'),
148 );
149
150
151 // Merge existing options with defaults
152 $existing_options = get_option('mxchat_options', array());
153 $merged_options = wp_parse_args($existing_options, $default_options);
154
155 // Update the options if they have changed
156 if ($existing_options !== $merged_options) {
157 update_option('mxchat_options', $merged_options);
158 }
159
160 // Add default limits for each role
161 $roles = wp_roles()->get_names();
162 foreach ($roles as $role_id => $role_name) {
163 $default_options['role_rate_limits'][$role_id] = esc_html__('100', 'mxchat');
164 }
165
166 return $default_options;
167
168 // Update the $this->options property
169 $this->options = $merged_options;
170 }
171
172 public function mxchat_add_plugin_page() {
173 // Main menu page
174 add_menu_page(
175 esc_html__('MxChat Settings', 'mxchat'),
176 esc_html__('MxChat', 'mxchat'),
177 'manage_options',
178 'mxchat-max',
179 array($this, 'mxchat_create_admin_page'),
180 'dashicons-testimonial',
181 6
182 );
183
184 // Submenu page for Knowledge
185 add_submenu_page(
186 'mxchat-max',
187 esc_html__('Prompts', 'mxchat'),
188 esc_html__('Knowledge', 'mxchat'),
189 'manage_options',
190 'mxchat-prompts',
191 array($this, 'mxchat_create_prompts_page')
192 );
193
194 add_submenu_page(
195 'mxchat-max',
196 esc_html__('Chat Transcripts', 'mxchat'),
197 esc_html__('Transcripts', 'mxchat'),
198 'manage_options',
199 'mxchat-transcripts',
200 array($this, 'mxchat_create_transcripts_page')
201 );
202
203 add_submenu_page(
204 'mxchat-max',
205 esc_html__('MxChat Actions', 'mxchat'),
206 esc_html__('Actions', 'mxchat'),
207 'manage_options',
208 'mxchat-actions',
209 array($this, 'mxchat_actions_page_html')
210 );
211
212 add_submenu_page(
213 'mxchat-max',
214 esc_html__('Add Ons', 'mxchat'),
215 esc_html__('Add Ons', 'mxchat'),
216 'manage_options',
217 'mxchat-addons',
218 array($this, 'mxchat_create_addons_page')
219 );
220
221 // Submenu page for Activation Key
222 add_submenu_page(
223 'mxchat-max',
224 esc_html__('Pro Upgrade', 'mxchat'),
225 esc_html__('Pro Upgrade', 'mxchat'),
226 'manage_options',
227 'mxchat-activation',
228 array($this, 'mxchat_create_activation_page')
229 );
230 }
231
232 public function mxchat_create_addons_page() {
233 require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php';
234 $addons_page = new MxChat_Addons();
235 $addons_page->render_page();
236 }
237
238 /**
239 * Test actual streaming functionality in the WordPress environment
240 */
241 public function mxchat_handle_test_streaming_actual() {
242 check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
243
244 // Check if headers have already been sent
245 if (headers_sent()) {
246 wp_send_json_error(['message' => 'Headers already sent - streaming not possible']);
247 return;
248 }
249
250 // Check for required functions
251 if (!function_exists('curl_init')) {
252 wp_send_json_error(['message' => 'cURL not available - streaming requires cURL']);
253 return;
254 }
255
256 // Get user's selected model and API key
257 $options = get_option('mxchat_options', []);
258 $selected_model = $options['model'] ?? 'gpt-4o';
259
260 // Get the provider from the model
261 $model_parts = explode('-', $selected_model);
262 $provider = strtolower($model_parts[0]);
263
264 // Get the appropriate API key
265 $api_key = '';
266 switch ($provider) {
267 case 'gpt':
268 case 'o1':
269 $api_key = $options['api_key'] ?? '';
270 break;
271 case 'claude':
272 $api_key = $options['claude_api_key'] ?? '';
273 break;
274 case 'grok':
275 $api_key = $options['xai_api_key'] ?? '';
276 break;
277 case 'deepseek':
278 $api_key = $options['deepseek_api_key'] ?? '';
279 break;
280 case 'gemini':
281 $api_key = $options['gemini_api_key'] ?? '';
282 break;
283 default:
284 // Default to OpenAI for unknown models
285 $api_key = $options['api_key'] ?? '';
286 $provider = 'gpt';
287 break;
288 }
289
290 if (empty($api_key)) {
291 wp_send_json_error(['message' => "API key not configured for {$provider} provider"]);
292 return;
293 }
294
295 // Test streaming with the selected model and provider
296 try {
297 $this->perform_streaming_test($provider, $selected_model, $api_key);
298 } catch (Exception $e) {
299 wp_send_json_error(['message' => 'Streaming test exception: ' . $e->getMessage()]);
300 }
301 }
302
303 /**
304 * Perform the actual streaming test
305 */
306 private function perform_streaming_test($provider, $model, $api_key) {
307 // Set streaming headers
308 header('Content-Type: text/event-stream');
309 header('Cache-Control: no-cache');
310 header('X-Accel-Buffering: no'); // Disable nginx buffering
311
312 // Prepare test message
313 $test_message = "Please respond with exactly: 'Streaming test successful!' - send this as a short response for testing.";
314
315 // Configure API request based on provider
316 $url = '';
317 $headers = [];
318 $body = [];
319
320 switch ($provider) {
321 case 'gpt':
322 case 'o1':
323 $url = 'https://api.openai.com/v1/chat/completions';
324 $headers = [
325 'Content-Type: application/json',
326 'Authorization: Bearer ' . $api_key
327 ];
328 $body = [
329 'model' => $model,
330 'messages' => [['role' => 'user', 'content' => $test_message]],
331 'max_tokens' => 50,
332 'temperature' => 0.3,
333 'stream' => true
334 ];
335 break;
336
337 case 'claude':
338 $url = 'https://api.anthropic.com/v1/messages';
339 $headers = [
340 'Content-Type: application/json',
341 'x-api-key: ' . $api_key,
342 'anthropic-version: 2023-06-01'
343 ];
344 $body = [
345 'model' => $model,
346 'messages' => [['role' => 'user', 'content' => $test_message]],
347 'max_tokens' => 50,
348 'temperature' => 0.3,
349 'stream' => true
350 ];
351 break;
352
353 case 'grok':
354 $url = 'https://api.x.ai/v1/chat/completions';
355 $headers = [
356 'Content-Type: application/json',
357 'Authorization: Bearer ' . $api_key
358 ];
359 $body = [
360 'model' => $model,
361 'messages' => [['role' => 'user', 'content' => $test_message]],
362 'max_tokens' => 50,
363 'temperature' => 0.3,
364 'stream' => true
365 ];
366 break;
367
368 case 'deepseek':
369 $url = 'https://api.deepseek.com/v1/chat/completions';
370 $headers = [
371 'Content-Type: application/json',
372 'Authorization: Bearer ' . $api_key
373 ];
374 $body = [
375 'model' => $model,
376 'messages' => [['role' => 'user', 'content' => $test_message]],
377 'max_tokens' => 50,
378 'temperature' => 0.3,
379 'stream' => true
380 ];
381 break;
382
383 default:
384 echo "data: " . json_encode(['error' => 'Unsupported provider for streaming test: ' . $provider]) . "\n\n";
385 flush();
386 return;
387 }
388
389 // Initialize cURL
390 $ch = curl_init();
391 curl_setopt($ch, CURLOPT_URL, $url);
392 curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
393 curl_setopt($ch, CURLOPT_POST, true);
394 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
395 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
396 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
397 curl_setopt($ch, CURLOPT_TIMEOUT, 30);
398 curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use ($provider) {
399 return $this->process_streaming_test_data($data, $provider);
400 });
401
402 // Send initial test message
403 echo "data: " . json_encode(['content' => '[Starting streaming test...]']) . "\n\n";
404 flush();
405
406 $result = curl_exec($ch);
407 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
408 $curl_error = curl_error($ch);
409 curl_close($ch);
410
411 if ($curl_error) {
412 echo "data: " . json_encode(['error' => 'cURL Error: ' . $curl_error]) . "\n\n";
413 flush();
414 return;
415 }
416
417 if ($http_code !== 200) {
418 echo "data: " . json_encode(['error' => 'API returned HTTP ' . $http_code]) . "\n\n";
419 flush();
420 return;
421 }
422
423 // Send completion signal
424 echo "data: [DONE]\n\n";
425 flush();
426 }
427
428 /**
429 * Process streaming data for the test
430 */
431 private function process_streaming_test_data($data, $provider) {
432 static $chunk_count = 0;
433
434 $lines = explode("\n", $data);
435
436 foreach ($lines as $line) {
437 if (trim($line) === '') {
438 continue;
439 }
440
441 // Handle different provider formats
442 if ($provider === 'claude') {
443 // Claude uses event: and data: format
444 if (strpos($line, 'data: ') === 0) {
445 $json_str = substr($line, 6);
446 $json = json_decode($json_str, true);
447
448 if (isset($json['type']) && $json['type'] === 'content_block_delta') {
449 if (isset($json['delta']['text'])) {
450 $chunk_count++;
451 echo "data: " . json_encode([
452 'content' => $json['delta']['text'],
453 'test_chunk' => $chunk_count
454 ]) . "\n\n";
455 flush();
456 }
457 }
458 }
459 } else {
460 // OpenAI, X.AI, DeepSeek format
461 if (strpos($line, 'data: ') === 0) {
462 $json_str = substr($line, 6);
463
464 if ($json_str === '[DONE]') {
465 // Don't echo [DONE] here, let the main function handle it
466 continue;
467 }
468
469 $json = json_decode($json_str, true);
470 if (isset($json['choices'][0]['delta']['content'])) {
471 $chunk_count++;
472 echo "data: " . json_encode([
473 'content' => $json['choices'][0]['delta']['content'],
474 'test_chunk' => $chunk_count
475 ]) . "\n\n";
476 flush();
477 }
478 }
479 }
480 }
481
482 return strlen($data);
483 }
484
485 /**
486 * Updated version of your existing test method (keep this as a fallback)
487 */
488 public function mxchat_handle_test_streaming() {
489 check_ajax_referer('mxchat_test_streaming_nonce', 'nonce');
490
491 // Use the actual streaming test instead
492 $this->mxchat_handle_test_streaming_actual();
493 }
494
495
496 public function register_pinecone_settings() {
497 register_setting(
498 'mxchat_pinecone_addon_options',
499 'mxchat_pinecone_addon_options',
500 array(
501 'type' => 'array',
502 'sanitize_callback' => array($this, 'sanitize_pinecone_settings'),
503 'default' => array(
504 'mxchat_use_pinecone' => '0',
505 'mxchat_pinecone_api_key' => '',
506 'mxchat_pinecone_host' => '',
507 'mxchat_pinecone_index' => '',
508 'mxchat_pinecone_environment' => ''
509 )
510 )
511 );
512 }
513
514 public function sanitize_pinecone_settings($input) {
515 $sanitized = array();
516
517 $sanitized['mxchat_use_pinecone'] = isset($input['mxchat_use_pinecone']) ? '1' : '0';
518 $sanitized['mxchat_pinecone_api_key'] = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
519 $sanitized['mxchat_pinecone_host'] = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
520 $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
521 $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
522
523 // Remove https:// from host if present
524 $sanitized['mxchat_pinecone_host'] = str_replace(['https://', 'http://'], '', $sanitized['mxchat_pinecone_host']);
525
526 return $sanitized;
527 }
528
529 public function mxchat_display_admin_notice() {
530 // Success notice
531 if ($message = get_transient('mxchat_admin_notice_success')) {
532 ?>
533 <div class="notice notice-success is-dismissible">
534 <p><?php echo esc_html($message); ?></p>
535 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
536 </div>
537 <?php
538 delete_transient('mxchat_admin_notice_success'); // Clear the transient after displaying
539 }
540
541 // Error notice
542 if ($message = get_transient('mxchat_admin_notice_error')) {
543 ?>
544 <div class="notice notice-error is-dismissible">
545 <p><?php echo esc_html($message); ?></p>
546 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php echo esc_html__('Dismiss this notice.', 'mxchat'); ?></span></button>
547 </div>
548 <?php
549 delete_transient('mxchat_admin_notice_error'); // Clear the transient after displaying
550 }
551 }
552
553 public function show_live_agent_disabled_banner() {
554 $show_disabled_notice = get_option('mxchat_show_live_agent_disabled_notice', false);
555
556 if ($show_disabled_notice) {
557 ?>
558 <div class="mxchat-live-agent-disabled-notice" id="mxchat-disabled-notice">
559 <div class="mxchat-pro-notification">
560 <button type="button" class="mxchat-dismiss-btn" onclick="dismissLiveAgentNotice()" aria-label="Dismiss notification">
561 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
562 <line x1="18" y1="6" x2="6" y2="18"></line>
563 <line x1="6" y1="6" x2="18" y2="18"></line>
564 </svg>
565 </button>
566 <div class="mxchat-live-agent-content">
567 <h3>🔧 Live Agent Integration Updated!</h3>
568 <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>
569 </div>
570 </div>
571 </div>
572 <?php
573 }
574 }
575 public function mxchat_create_admin_page() {
576 $this->add_live_agent_nonce();
577
578 ?>
579 <div class="wrap mxchat-wrapper">
580 <!-- Hero Section -->
581 <div class="mxchat-hero">
582 <h1 class="mxchat-main-title">
583 <span class="mxchat-gradient-text">MxChat</span> Settings
584 </h1>
585 <p class="mxchat-hero-subtitle">
586 <?php esc_html_e('Configure your AI chatbot, manage integrations and explore tutorials to get the most out of MxChat.', 'mxchat'); ?>
587 </p>
588 </div>
589
590 <div class="mxchat-content">
591
592 <?php if (!$this->is_activated): ?>
593 <div class="mxchat-pro-card">
594 <div class="mxchat-pro-notification">
595 <div class="mxchat-pro-content">
596 <h3>🚀 Limited Lifetime Offer: Save 15% on MxChat Pro, Agency, or Agency Plus!</h3>
597 <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, multi-bot, and more – all included with your <strong>lifetime license!</strong></p> </div>
598 <div class="mxchat-pro-cta">
599 <a href="https://mxchat.ai/" target="_blank" class="mxchat-button"><?php echo esc_html__('Upgrade Today', 'mxchat'); ?></a>
600 <a href="<?php echo admin_url('admin.php?page=mxchat-addons'); ?>" class="mxchat-link"><?php echo esc_html__('Preview Add-ons', 'mxchat'); ?></a>
601 </div>
602 </div>
603 </div>
604 <?php endif; ?>
605
606 <?php
607 $this->show_live_agent_disabled_banner();
608 ?>
609
610 <!-- Tabs Navigation -->
611 <div class="mxchat-tabs">
612 <button class="mxchat-tab-button active" data-tab="chatbot"><?php echo esc_html__('Chatbot', 'mxchat'); ?></button>
613 <button class="mxchat-tab-button" data-tab="api-keys"><?php echo esc_html__('API Keys', 'mxchat'); ?></button>
614 <button class="mxchat-tab-button" data-tab="embed"><?php echo esc_html__('Toolbar & Components', 'mxchat'); ?></button>
615 <button class="mxchat-tab-button" data-tab="general"><?php echo esc_html__('YouTube Tutorials', 'mxchat'); ?></button>
616 </div>
617
618 <!-- Tab Contents -->
619 <div id="chatbot" class="mxchat-tab-content active">
620 <div class="mxchat-card">
621 <div class="mxchat-autosave-section">
622 <?php do_settings_sections('mxchat-chatbot'); ?>
623 </div>
624 </div>
625 </div>
626
627 <div id="api-keys" class="mxchat-tab-content">
628 <div class="mxchat-card">
629 <div class="mxchat-autosave-section">
630 <table class="form-table">
631 <?php do_settings_fields('mxchat-api-keys', 'mxchat_api_keys_section'); ?>
632 </table>
633 </div>
634 </div>
635 </div>
636
637 <div id="embed" class="mxchat-tab-content">
638
639 <div class="mxchat-card">
640 <h2><?php esc_html_e('Toolbar Settings', 'mxchat'); ?></h2>
641 <div class="mxchat-autosave-section">
642 <table class="form-table">
643 <?php do_settings_fields('mxchat-embed', 'mxchat_pdf_intent_section'); ?>
644 </table>
645 </div>
646 </div>
647
648
649 <div class="mxchat-card">
650 <h2><?php esc_html_e('Loops Settings', 'mxchat'); ?></h2>
651 <div class="mxchat-autosave-section">
652 <table class="form-table">
653 <?php do_settings_fields('mxchat-embed', 'mxchat_loops_section'); ?>
654 </table>
655 </div>
656 </div>
657
658 <div class="mxchat-card">
659 <h2><?php esc_html_e('Brave Search Settings', 'mxchat'); ?></h2>
660 <div class="mxchat-autosave-section">
661 <table class="form-table">
662 <?php do_settings_fields('mxchat-embed', 'mxchat_brave_section'); ?>
663 </table>
664 </div>
665 </div>
666
667 <div class="mxchat-card">
668 <h2><?php esc_html_e('Live Agent Settings', 'mxchat'); ?></h2>
669 <div class="mxchat-autosave-section">
670 <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">
671 <?php do_settings_fields('mxchat-embed', 'mxchat_live_agent_section'); ?>
672 </table>
673 </div>
674 </div>
675 </div>
676
677 <div id="general" class="mxchat-tab-content">
678 <div class="mxchat-card">
679 <?php do_settings_sections('mxchat-general'); ?>
680 <div class="video-tutorials-section">
681
682
683 <div class="support-notification">
684 <div class="support-notification-icon">
685 <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">
686 <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"/>
687 <path d="M12 9v4"/>
688 <path d="M12 17h.01"/>
689 </svg>
690 </div>
691 <div class="support-notification-content">
692 <h3 class="support-notification-title"><?php echo esc_html__('Need Help? We\'re Here for You!', 'mxchat'); ?></h3>
693 <p class="support-notification-message">
694 <?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'); ?>
695 </p>
696 <a href="https://wordpress.org/support/plugin/mxchat-basic/" target="_blank" rel="noopener noreferrer" class="support-notification-button">
697 <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">
698 <path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/>
699 <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/>
700 </svg>
701 <?php echo esc_html__('Submit Support Ticket', 'mxchat'); ?>
702 </a>
703 </div>
704 </div>
705
706 <div class="tutorial-grid">
707
708 <div class="tutorial-item">
709 <h3><?php echo esc_html__('AI Theme Generator Tutorial', 'mxchat'); ?></h3>
710 <div class="video-description">
711 <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>
712 <a href="https://www.youtube.com/watch?v=rSQDW2qbtRU&t" target="_blank" rel="noopener" class="video-link">
713 <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>
714 <?php echo esc_html__('Watch AI Theme Generator Tutorial', 'mxchat'); ?>
715 </a>
716 </div>
717 </div>
718
719
720 <div class="tutorial-item">
721 <h3><?php echo esc_html__('MxChat Forms Tutorial', 'mxchat'); ?></h3>
722 <div class="video-description">
723 <p><?php echo esc_html__('Learn how to create and manage smart forms that automatically trigger during chat conversations.', 'mxchat'); ?></p>
724 <a href="https://www.youtube.com/watch?v=3MrWy5dRalA" target="_blank" rel="noopener" class="video-link">
725 <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>
726 <?php echo esc_html__('Watch MxChat Forms Tutorial', 'mxchat'); ?>
727 </a>
728 </div>
729 </div>
730
731 <div class="tutorial-item">
732 <h3><?php echo esc_html__('Admin Assistant Add-on', 'mxchat'); ?></h3>
733 <div class="video-description">
734 <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>
735 <a href="https://youtu.be/AdEA1k-UCFM" target="_blank" rel="noopener" class="video-link">
736 <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>
737 <?php echo esc_html__('Watch Admin Assistant Tutorial', 'mxchat'); ?>
738 </a>
739 </div>
740 </div>
741
742 <div class="tutorial-item">
743 <h3><?php echo esc_html__('Intent Tester Guide', 'mxchat'); ?></h3>
744 <div class="video-description">
745 <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>
746 <a href="https://www.youtube.com/watch?v=uTr14tn59Hc" target="_blank" rel="noopener" class="video-link">
747 <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>
748 <?php echo esc_html__('Watch Intent Tester Tutorial', 'mxchat'); ?>
749 </a>
750 </div>
751 </div>
752
753 <div class="tutorial-item">
754 <h3><?php echo esc_html__('Theme Customizer Add-on', 'mxchat'); ?></h3>
755 <div class="video-description">
756 <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>
757 <a href="https://youtu.be/MfbB9mZi6ag" target="_blank" rel="noopener" class="video-link">
758 <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>
759 <?php echo esc_html__('Watch Theme Customizer Tutorial', 'mxchat'); ?>
760 </a>
761 </div>
762 </div>
763
764 <div class="tutorial-item">
765 <h3><?php echo esc_html__('WooCommerce Integration', 'mxchat'); ?></h3>
766 <div class="video-description">
767 <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>
768 <a href="https://www.youtube.com/watch?v=WsqAppHRGdA" target="_blank" rel="noopener" class="video-link">
769 <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>
770 <?php echo esc_html__('Watch WooCommerce Integration Tutorial', 'mxchat'); ?>
771 </a>
772 </div>
773 </div>
774
775 <div class="tutorial-item">
776 <h3><?php echo esc_html__('Knowledge Base Setup', 'mxchat'); ?></h3>
777 <div class="video-description">
778 <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>
779 <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
780 <a href="https://www.youtube.com/watch?v=8Ztjs66-VTo" target="_blank" rel="noopener" class="video-link">
781 <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>
782 <?php echo esc_html__('Watch Knowledge Base Setup Tutorial', 'mxchat'); ?>
783 </a>
784 </div>
785 </div>
786
787 <div class="tutorial-item">
788 <h3><?php echo esc_html__('Toolbar Chat with Documents', 'mxchat'); ?></h3>
789 <div class="video-description">
790 <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>
791 <a href="https://www.youtube.com/watch?v=j_c45WWCTG0" target="_blank" rel="noopener" class="video-link">
792 <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>
793 <?php echo esc_html__('Watch Document Chat Tutorial', 'mxchat'); ?>
794 </a>
795 </div>
796 </div>
797
798 <div class="tutorial-item">
799 <h3><?php echo esc_html__('Perplexity Integration', 'mxchat'); ?></h3>
800 <div class="video-description">
801 <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>
802 <a href="https://youtu.be/wpKkbt24-bo" target="_blank" rel="noopener" class="video-link">
803 <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>
804 <?php echo esc_html__('Watch Perplexity Integration Tutorial', 'mxchat'); ?>
805 </a>
806 </div>
807 </div>
808
809 <div class="tutorial-item">
810 <h3><?php echo esc_html__('Brave Search Intent', 'mxchat'); ?></h3>
811 <div class="video-description">
812 <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>
813 <a href="https://www.youtube.com/watch?v=7vDL5H7vToc" target="_blank" rel="noopener" class="video-link">
814 <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>
815 <?php echo esc_html__('Watch Brave Search Intent Tutorial', 'mxchat'); ?>
816 </a>
817 </div>
818 </div>
819
820 <div class="tutorial-item">
821 <h3><?php echo esc_html__('Loops Email Capture', 'mxchat'); ?></h3>
822 <div class="video-description">
823 <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>
824 <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
825 <a href="https://www.youtube.com/watch?v=CNgm5TYDyTc" target="_blank" rel="noopener" class="video-link">
826 <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>
827 <?php echo esc_html__('Watch Loops Email Capture Tutorial', 'mxchat'); ?>
828 </a>
829 </div>
830 </div>
831
832 <div class="tutorial-item">
833 <h3><?php echo esc_html__('MxChat AI Agent Testing Service', 'mxchat'); ?></h3>
834 <div class="video-description">
835 <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>
836 <p><small><?php echo esc_html__('Note: This tutorial uses an older UI, but the process remains the same.', 'mxchat'); ?></small></p>
837 <a href="https://www.youtube.com/watch?v=A0jowbpyX54" target="_blank" rel="noopener" class="video-link">
838 <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>
839 <?php echo esc_html__('Watch AI Agent Testing Tutorial', 'mxchat'); ?>
840 </a>
841 </div>
842 </div>
843 </div>
844
845 </div>
846 </div>
847 </div>
848 </div>
849 </div>
850 <?php
851 }
852
853 public function dismiss_live_agent_notice() {
854 // Add debugging
855 //error_log('dismiss_live_agent_notice called');
856 //error_log('POST data: ' . print_r($_POST, true));
857
858 // Verify nonce
859 if (!wp_verify_nonce($_POST['nonce'], 'dismiss_live_agent_notice')) {
860 //error_log('Nonce verification failed');
861 wp_die('Security check failed');
862 }
863
864 // Remove the notice flag
865 $deleted = delete_option('mxchat_show_live_agent_disabled_notice');
866 //error_log('Option deleted: ' . ($deleted ? 'yes' : 'no'));
867
868 wp_send_json_success();
869 }
870 public function add_live_agent_nonce() {
871 if (get_option('mxchat_show_live_agent_disabled_notice', false)) {
872 // Make sure your admin script is enqueued and localize the data
873 wp_localize_script('mxchat-admin-js', 'mxchatLiveAgent', array(
874 'nonce' => wp_create_nonce('dismiss_live_agent_notice'),
875 'ajaxurl' => admin_url('admin-ajax.php')
876 ));
877 }
878 }
879
880
881 public function mxchat_create_transcripts_page() {
882 global $wpdb;
883 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
884
885 // Get basic stats
886 $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
887 $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
888
889 // Count unique users with detailed breakdown
890 $total_users = $wpdb->get_var("
891 SELECT COUNT(DISTINCT
892 CASE
893 WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
894 WHEN user_id != 0 THEN CONCAT('user_', user_id)
895 WHEN user_identifier NOT LIKE 'Tech-Savvy User'
896 AND user_identifier NOT LIKE 'Detail-Oriented User'
897 AND user_identifier NOT LIKE 'Language Learner'
898 AND user_identifier NOT LIKE 'Casual Browser'
899 AND user_identifier NOT LIKE 'Policy Enforcer'
900 AND user_identifier NOT LIKE 'Researcher'
901 AND user_identifier NOT LIKE 'Loyalty Member'
902 AND user_identifier NOT LIKE 'Gift Buyer'
903 AND user_identifier NOT LIKE 'Parent or Caregiver'
904 THEN user_identifier
905 ELSE session_id
906 END
907 )
908 FROM $table_name
909 WHERE role != 'assistant'
910 ");
911
912 // Get user type breakdown
913 $registered_users = $wpdb->get_var("
914 SELECT COUNT(DISTINCT user_email)
915 FROM $table_name
916 WHERE user_email != '' AND user_email IS NOT NULL
917 ");
918
919 $guest_users = $wpdb->get_var("
920 SELECT COUNT(DISTINCT user_identifier)
921 FROM $table_name
922 WHERE (user_email = '' OR user_email IS NULL)
923 AND role != 'assistant'
924 AND user_identifier NOT LIKE 'Tech-Savvy User'
925 AND user_identifier NOT LIKE 'Detail-Oriented User'
926 AND user_identifier NOT LIKE 'Language Learner'
927 AND user_identifier NOT LIKE 'Casual Browser'
928 AND user_identifier NOT LIKE 'Policy Enforcer'
929 AND user_identifier NOT LIKE 'Researcher'
930 AND user_identifier NOT LIKE 'Loyalty Member'
931 AND user_identifier NOT LIKE 'Gift Buyer'
932 AND user_identifier NOT LIKE 'Parent or Caregiver'
933 ");
934
935 // Get agent test messages count
936 $agent_tests = $wpdb->get_var("
937 SELECT COUNT(DISTINCT session_id)
938 FROM $table_name
939 WHERE user_identifier IN (
940 'Tech-Savvy User',
941 'Detail-Oriented User',
942 'Language Learner',
943 'Casual Browser',
944 'Policy Enforcer',
945 'Researcher',
946 'Loyalty Member',
947 'Gift Buyer',
948 'Parent or Caregiver'
949 )
950 ");
951 // Get activity metrics
952 $today_chats = $wpdb->get_var("
953 SELECT COUNT(DISTINCT session_id)
954 FROM $table_name
955 WHERE DATE(timestamp) = CURDATE()
956 ");
957
958 $week_chats = $wpdb->get_var("
959 SELECT COUNT(DISTINCT session_id)
960 FROM $table_name
961 WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)
962 ");
963
964 $month_chats = $wpdb->get_var("
965 SELECT COUNT(DISTINCT session_id)
966 FROM $table_name
967 WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
968 ");
969
970 // Get daily chat data for last 7 days
971 $daily_stats = $wpdb->get_results("
972 SELECT
973 DATE(timestamp) as date,
974 COUNT(DISTINCT session_id) as chats,
975 COUNT(*) as messages
976 FROM $table_name
977 WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)
978 GROUP BY DATE(timestamp)
979 ORDER BY date ASC
980 ");
981
982 // Get average messages per chat
983 $avg_messages = $wpdb->get_var("
984 SELECT AVG(message_count)
985 FROM (
986 SELECT session_id, COUNT(*) as message_count
987 FROM $table_name
988 GROUP BY session_id
989 ) as chat_counts
990 ");
991 $avg_messages = $avg_messages ? round($avg_messages, 1) : 0;
992
993 // Get busiest hour
994 $busiest_hour = $wpdb->get_row("
995 SELECT HOUR(timestamp) as hour, COUNT(DISTINCT session_id) as chat_count
996 FROM $table_name
997 GROUP BY HOUR(timestamp)
998 ORDER BY chat_count DESC
999 LIMIT 1
1000 ");
1001
1002 // Prepare chart data
1003 $chart_labels = array();
1004 $chart_chats = array();
1005 $chart_messages = array();
1006
1007 // Fill last 7 days with data
1008 for ($i = 6; $i >= 0; $i--) {
1009 $date = date('Y-m-d', strtotime("-$i days"));
1010 $day_name = date('D', strtotime("-$i days"));
1011 $chart_labels[] = $day_name;
1012
1013 $found = false;
1014 foreach ($daily_stats as $stat) {
1015 if ($stat->date === $date) {
1016 $chart_chats[] = (int)$stat->chats;
1017 $chart_messages[] = (int)$stat->messages;
1018 $found = true;
1019 break;
1020 }
1021 }
1022 if (!$found) {
1023 $chart_chats[] = 0;
1024 $chart_messages[] = 0;
1025 }
1026 }
1027 ?>
1028 <div class="wrap mxchat-transcripts-wrapper">
1029 <!-- Hero Section -->
1030 <div class="mxchat-transcripts-hero">
1031 <h1 class="mxchat-main-title">
1032 Chat <span class="mxchat-gradient-text">Transcripts</span>
1033 </h1>
1034 <p class="mxchat-hero-subtitle">
1035 <?php esc_html_e('Review and manage your chatbot conversations with detailed message history.', 'mxchat'); ?>
1036 </p>
1037 </div>
1038 <div class="mxchat-content">
1039 <!-- Enhanced Metrics Dashboard -->
1040 <div class="mxchat-metrics-dashboard">
1041 <!-- Tab Navigation -->
1042 <div class="mxchat-metrics-tabs">
1043 <button class="mxchat-metrics-tab active" data-tab="overview">
1044 <span class="tab-icon">📊</span>
1045 <span class="tab-label">Overview</span>
1046 </button>
1047 <button class="mxchat-metrics-tab" data-tab="activity">
1048 <span class="tab-icon">📈</span>
1049 <span class="tab-label">Activity</span>
1050 </button>
1051 <button class="mxchat-metrics-tab" data-tab="insights">
1052 <span class="tab-icon">💡</span>
1053 <span class="tab-label">Insights</span>
1054 </button>
1055 </div>
1056
1057 <!-- Tab Content -->
1058 <div class="mxchat-metrics-content">
1059 <!-- Overview Tab -->
1060 <div class="mxchat-metrics-panel active" data-panel="overview">
1061 <div class="mxchat-stats-grid">
1062 <div class="mxchat-stat-card mxchat-stat-primary">
1063 <div class="stat-icon">💬</div>
1064 <div class="stat-content">
1065 <span class="stat-value"><?php echo esc_html($total_chats); ?></span>
1066 <span class="stat-label"><?php esc_html_e('Total Chats', 'mxchat'); ?></span>
1067 </div>
1068 </div>
1069 <div class="mxchat-stat-card mxchat-stat-primary">
1070 <div class="stat-icon">📝</div>
1071 <div class="stat-content">
1072 <span class="stat-value"><?php echo esc_html($total_messages); ?></span>
1073 <span class="stat-label"><?php esc_html_e('Total Messages', 'mxchat'); ?></span>
1074 </div>
1075 </div>
1076 <div class="mxchat-stat-card mxchat-stat-primary">
1077 <div class="stat-icon">👥</div>
1078 <div class="stat-content">
1079 <span class="stat-value"><?php echo esc_html($total_users); ?></span>
1080 <span class="stat-label"><?php esc_html_e('Unique Users', 'mxchat'); ?></span>
1081 <span class="stat-sublabel">
1082 <?php
1083 echo sprintf(
1084 esc_html__('%d registered • %d guests • %d tests', 'mxchat'),
1085 $registered_users,
1086 $guest_users,
1087 $agent_tests
1088 );
1089 ?>
1090 </span>
1091 </div>
1092 </div>
1093 <div class="mxchat-stat-card mxchat-stat-primary">
1094 <div class="stat-icon">💭</div>
1095 <div class="stat-content">
1096 <span class="stat-value"><?php echo esc_html($avg_messages); ?></span>
1097 <span class="stat-label"><?php esc_html_e('Avg Messages/Chat', 'mxchat'); ?></span>
1098 </div>
1099 </div>
1100 </div>
1101 </div>
1102
1103 <!-- Activity Tab -->
1104 <div class="mxchat-metrics-panel" data-panel="activity">
1105 <div class="mxchat-activity-layout">
1106 <div class="mxchat-activity-stats">
1107 <div class="mxchat-stat-card mxchat-stat-accent">
1108 <div class="stat-icon">📅</div>
1109 <div class="stat-content">
1110 <span class="stat-value"><?php echo esc_html($today_chats); ?></span>
1111 <span class="stat-label"><?php esc_html_e('Today', 'mxchat'); ?></span>
1112 </div>
1113 </div>
1114 <div class="mxchat-stat-card mxchat-stat-accent">
1115 <div class="stat-icon">📆</div>
1116 <div class="stat-content">
1117 <span class="stat-value"><?php echo esc_html($week_chats); ?></span>
1118 <span class="stat-label"><?php esc_html_e('Last 7 Days', 'mxchat'); ?></span>
1119 </div>
1120 </div>
1121 <div class="mxchat-stat-card mxchat-stat-accent">
1122 <div class="stat-icon">🗓️</div>
1123 <div class="stat-content">
1124 <span class="stat-value"><?php echo esc_html($month_chats); ?></span>
1125 <span class="stat-label"><?php esc_html_e('Last 30 Days', 'mxchat'); ?></span>
1126 </div>
1127 </div>
1128 </div>
1129
1130 <div class="mxchat-chart-container">
1131 <h3 class="mxchat-chart-title"><?php esc_html_e('7-Day Activity Trend', 'mxchat'); ?></h3>
1132 <canvas id="mxchat-activity-chart"></canvas>
1133 </div>
1134 </div>
1135 </div>
1136
1137 <!-- Insights Tab -->
1138 <div class="mxchat-metrics-panel" data-panel="insights">
1139 <div class="mxchat-insights-grid">
1140 <div class="mxchat-insight-card">
1141 <div class="insight-header">
1142 <span class="insight-icon">🕐</span>
1143 <h3><?php esc_html_e('Peak Activity', 'mxchat'); ?></h3>
1144 </div>
1145 <div class="insight-content">
1146 <div class="insight-value">
1147 <?php
1148 if ($busiest_hour) {
1149 $hour = $busiest_hour->hour;
1150 $formatted_hour = date('g A', strtotime("$hour:00"));
1151 echo esc_html($formatted_hour);
1152 } else {
1153 echo esc_html__('N/A', 'mxchat');
1154 }
1155 ?>
1156 </div>
1157 <div class="insight-description">
1158 <?php esc_html_e('Most active hour of the day', 'mxchat'); ?>
1159 </div>
1160 </div>
1161 </div>
1162
1163 <div class="mxchat-insight-card">
1164 <div class="insight-header">
1165 <span class="insight-icon">📊</span>
1166 <h3><?php esc_html_e('Engagement Rate', 'mxchat'); ?></h3>
1167 </div>
1168 <div class="insight-content">
1169 <div class="insight-value">
1170 <?php
1171 $engagement_rate = $total_chats > 0 ? round(($total_messages / $total_chats), 1) : 0;
1172 echo esc_html($engagement_rate);
1173 ?>
1174 </div>
1175 <div class="insight-description">
1176 <?php esc_html_e('Messages exchanged per chat', 'mxchat'); ?>
1177 </div>
1178 </div>
1179 </div>
1180
1181 <div class="mxchat-insight-card">
1182 <div class="insight-header">
1183 <span class="insight-icon">👤</span>
1184 <h3><?php esc_html_e('User Distribution', 'mxchat'); ?></h3>
1185 </div>
1186 <div class="insight-content">
1187 <div class="insight-breakdown">
1188 <div class="breakdown-item">
1189 <span class="breakdown-label"><?php esc_html_e('Registered', 'mxchat'); ?></span>
1190 <span class="breakdown-value">
1191 <?php
1192 $registered_pct = $total_users > 0 ? round(($registered_users / $total_users) * 100) : 0;
1193 echo esc_html($registered_pct . '%');
1194 ?>
1195 </span>
1196 </div>
1197 <div class="breakdown-item">
1198 <span class="breakdown-label"><?php esc_html_e('Guests', 'mxchat'); ?></span>
1199 <span class="breakdown-value">
1200 <?php
1201 $guest_pct = $total_users > 0 ? round(($guest_users / $total_users) * 100) : 0;
1202 echo esc_html($guest_pct . '%');
1203 ?>
1204 </span>
1205 </div>
1206 </div>
1207 </div>
1208 </div>
1209
1210 <div class="mxchat-insight-card">
1211 <div class="insight-header">
1212 <span class="insight-icon">⚡</span>
1213 <h3><?php esc_html_e('Activity Status', 'mxchat'); ?></h3>
1214 </div>
1215 <div class="insight-content">
1216 <div class="insight-value insight-status">
1217 <?php
1218 if ($today_chats > 0) {
1219 echo '<span class="status-badge status-active">' . esc_html__('Active', 'mxchat') . '</span>';
1220 } else if ($week_chats > 0) {
1221 echo '<span class="status-badge status-moderate">' . esc_html__('Moderate', 'mxchat') . '</span>';
1222 } else {
1223 echo '<span class="status-badge status-quiet">' . esc_html__('Quiet', 'mxchat') . '</span>';
1224 }
1225 ?>
1226 </div>
1227 <div class="insight-description">
1228 <?php
1229 if ($today_chats > 0) {
1230 echo esc_html(sprintf(__('%d chats today', 'mxchat'), $today_chats));
1231 } else if ($week_chats > 0) {
1232 echo esc_html(sprintf(__('%d chats this week', 'mxchat'), $week_chats));
1233 } else {
1234 echo esc_html__('No recent activity', 'mxchat');
1235 }
1236 ?>
1237 </div>
1238 </div>
1239 </div>
1240 </div>
1241 </div>
1242 </div>
1243 </div>
1244
1245 <script>
1246 var mxchatChartData = {
1247 labels: <?php echo json_encode($chart_labels); ?>,
1248 chats: <?php echo json_encode($chart_chats); ?>,
1249 messages: <?php echo json_encode($chart_messages); ?>
1250 };
1251 </script>
1252
1253 <!-- Search and Filter Controls with Notification Settings Button -->
1254 <div class="mxchat-controls-wrapper">
1255 <div class="mxchat-search-box">
1256 <input type="text" id="mxchat-search-transcripts"
1257 placeholder="<?php esc_attr_e('Search transcripts...', 'mxchat'); ?>"
1258 class="regular-text">
1259 </div>
1260 <form id="mxchat-delete-form" method="post">
1261 <?php wp_nonce_field('mxchat_delete_chat_history', 'mxchat_delete_chat_nonce'); ?>
1262 <div class="mxchat-controls">
1263 <button type="button" id="mxchat-chat-email-notification-btn" class="mxchat-action-button">
1264 <span class="dashicons dashicons-admin-generic"></span>
1265 <?php esc_html_e('Settings', 'mxchat'); ?>
1266 </button>
1267 <button type="button" id="mxchat-export-transcripts" class="mxchat-action-button">
1268 <span class="dashicons dashicons-download"></span>
1269 <?php esc_html_e('Export All Chats', 'mxchat'); ?>
1270 </button>
1271 <button type="button" id="mxchat-select-all-transcripts" class="mxchat-select-button">
1272 <span class="dashicons dashicons-yes-alt"></span>
1273 <span class="button-text"><?php esc_html_e('Select All', 'mxchat'); ?></span>
1274 </button>
1275 <button type="submit" class="button delete-chats-button">
1276 <span class="dashicons dashicons-trash"></span>
1277 <?php esc_html_e('Delete Selected', 'mxchat'); ?>
1278 </button>
1279 </div>
1280 </form>
1281 </div>
1282
1283 <!-- Transcripts Container -->
1284 <div id="mxchat-transcripts"></div>
1285 </div>
1286 </div>
1287
1288 <!-- Modal for Chat Email Notification Settings -->
1289 <div id="mxchat-chat-email-notification-modal" class="mxchat-chat-notification-modal-overlay" style="display: none;">
1290 <div class="mxchat-chat-notification-modal-content">
1291 <div class="mxchat-chat-notification-modal-header">
1292 <h2><?php esc_html_e('Transcript Settings', 'mxchat'); ?></h2>
1293 <button type="button" class="mxchat-chat-notification-modal-close">&times;</button>
1294 </div>
1295 <div class="mxchat-chat-notification-modal-body">
1296 <form method="post" action="options.php" id="mxchat-chat-email-notification-form">
1297 <?php
1298 settings_fields('mxchat_transcripts_options');
1299 do_settings_sections('mxchat-transcripts');
1300 ?>
1301 <div class="mxchat-chat-notification-modal-footer">
1302 <button type="submit" class="button button-primary">
1303 <?php esc_html_e('Save Settings', 'mxchat'); ?>
1304 </button>
1305 <button type="button" class="button mxchat-chat-notification-modal-cancel">
1306 <?php esc_html_e('Cancel', 'mxchat'); ?>
1307 </button>
1308 </div>
1309 </form>
1310 </div>
1311 </div>
1312 </div>
1313 <?php
1314 }
1315
1316
1317 public function mxchat_transcripts_notification_section_callback() {
1318 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>';
1319 }
1320 public function mxchat_enable_notifications_callback() {
1321 $options = get_option('mxchat_transcripts_options', array());
1322 $enabled = isset($options['mxchat_enable_notifications']) ? $options['mxchat_enable_notifications'] : 0;
1323 ?>
1324 <label for="mxchat_enable_notifications">
1325 <input type="checkbox" id="mxchat_enable_notifications"
1326 name="mxchat_transcripts_options[mxchat_enable_notifications]"
1327 value="1" <?php checked(1, $enabled); ?>>
1328 <?php esc_html_e('Send email notification when a new chat session starts', 'mxchat'); ?>
1329 </label>
1330 <p class="description">
1331 <?php esc_html_e('Enable this option to receive email notifications for new chat sessions.', 'mxchat'); ?>
1332 </p>
1333 <?php
1334 }
1335 public function mxchat_notification_email_callback() {
1336 $options = get_option('mxchat_transcripts_options', array());
1337 $email = isset($options['mxchat_notification_email']) ? $options['mxchat_notification_email'] : get_option('admin_email');
1338 ?>
1339 <input type="email" id="mxchat_notification_email"
1340 name="mxchat_transcripts_options[mxchat_notification_email]"
1341 value="<?php echo esc_attr($email); ?>"
1342 class="regular-text">
1343 <p class="description">
1344 <?php esc_html_e('Enter the email address where notifications should be sent. Defaults to the admin email address.', 'mxchat'); ?>
1345 </p>
1346 <?php
1347 }
1348
1349 public function mxchat_auto_delete_transcripts_callback() {
1350 $options = get_option('mxchat_transcripts_options', array());
1351 $interval = isset($options['mxchat_auto_delete_transcripts']) ? $options['mxchat_auto_delete_transcripts'] : 'never';
1352 ?>
1353 <select id="mxchat_auto_delete_transcripts"
1354 name="mxchat_transcripts_options[mxchat_auto_delete_transcripts]">
1355 <option value="never" <?php selected($interval, 'never'); ?>>
1356 <?php esc_html_e('Never (Keep All Transcripts)', 'mxchat'); ?>
1357 </option>
1358 <option value="1week" <?php selected($interval, '1week'); ?>>
1359 <?php esc_html_e('After 1 Week', 'mxchat'); ?>
1360 </option>
1361 <option value="2weeks" <?php selected($interval, '2weeks'); ?>>
1362 <?php esc_html_e('After 2 Weeks', 'mxchat'); ?>
1363 </option>
1364 <option value="1month" <?php selected($interval, '1month'); ?>>
1365 <?php esc_html_e('After 1 Month', 'mxchat'); ?>
1366 </option>
1367 </select>
1368 <p class="description">
1369 <?php esc_html_e('Automatically delete old chat transcripts after the selected time period. This helps manage database size and privacy.', 'mxchat'); ?>
1370 </p>
1371 <?php
1372 }
1373
1374 public function mxchat_auto_email_transcript_callback() {
1375 $options = get_option('mxchat_transcripts_options', array());
1376 $enabled = isset($options['mxchat_auto_email_transcript_enabled']) ? $options['mxchat_auto_email_transcript_enabled'] : 0;
1377 $delay = isset($options['mxchat_auto_email_transcript_delay']) ? $options['mxchat_auto_email_transcript_delay'] : '30';
1378 $require_contact = isset($options['mxchat_auto_email_transcript_require_contact']) ? $options['mxchat_auto_email_transcript_require_contact'] : 0;
1379 ?>
1380 <label>
1381 <input type="checkbox"
1382 id="mxchat_auto_email_transcript_enabled"
1383 name="mxchat_transcripts_options[mxchat_auto_email_transcript_enabled]"
1384 value="1"
1385 <?php checked($enabled, 1); ?>>
1386 <?php esc_html_e('Enable Auto-Email of Full Transcript', 'mxchat'); ?>
1387 </label>
1388 <br><br>
1389 <label for="mxchat_auto_email_transcript_delay">
1390 <?php esc_html_e('Send transcript after:', 'mxchat'); ?>
1391 </label>
1392 <select id="mxchat_auto_email_transcript_delay"
1393 name="mxchat_transcripts_options[mxchat_auto_email_transcript_delay]">
1394 <option value="15" <?php selected($delay, '15'); ?>>
1395 <?php esc_html_e('15 minutes', 'mxchat'); ?>
1396 </option>
1397 <option value="30" <?php selected($delay, '30'); ?>>
1398 <?php esc_html_e('30 minutes', 'mxchat'); ?>
1399 </option>
1400 <option value="60" <?php selected($delay, '60'); ?>>
1401 <?php esc_html_e('1 hour', 'mxchat'); ?>
1402 </option>
1403 </select>
1404 <br><br>
1405 <label>
1406 <input type="checkbox"
1407 id="mxchat_auto_email_transcript_require_contact"
1408 name="mxchat_transcripts_options[mxchat_auto_email_transcript_require_contact]"
1409 value="1"
1410 <?php checked($require_contact, 1); ?>>
1411 <?php esc_html_e('Only send if visitor provided contact info', 'mxchat'); ?>
1412 </label>
1413 <p class="description">
1414 <?php esc_html_e('Automatically email the full transcript as a .txt file after the specified time has passed since the last user message. The scheduled email will be cancelled if a new message is received.', 'mxchat'); ?>
1415 <br>
1416 <?php esc_html_e('When "Only send if visitor provided contact info" is enabled, transcripts will only be emailed if the visitor shared an email address or phone number (including WhatsApp) in the chat.', 'mxchat'); ?>
1417 </p>
1418 <?php
1419 }
1420
1421
1422
1423 public function sanitize_transcripts_options($input) {
1424 $sanitized = array();
1425
1426 $sanitized['mxchat_enable_notifications'] = isset($input['mxchat_enable_notifications']) ? 1 : 0;
1427
1428 if (isset($input['mxchat_notification_email'])) {
1429 $sanitized['mxchat_notification_email'] = sanitize_email($input['mxchat_notification_email']);
1430 if (!is_email($sanitized['mxchat_notification_email'])) {
1431 add_settings_error(
1432 'mxchat_transcripts_options',
1433 'invalid_email',
1434 __('Please enter a valid email address for notifications.', 'mxchat'),
1435 'error'
1436 );
1437 $sanitized['mxchat_notification_email'] = get_option('admin_email');
1438 }
1439 }
1440
1441 // Sanitize auto-delete setting
1442 $valid_intervals = array('never', '1week', '2weeks', '1month');
1443 if (isset($input['mxchat_auto_delete_transcripts'])) {
1444 $sanitized['mxchat_auto_delete_transcripts'] = in_array($input['mxchat_auto_delete_transcripts'], $valid_intervals)
1445 ? $input['mxchat_auto_delete_transcripts']
1446 : 'never';
1447 } else {
1448 $sanitized['mxchat_auto_delete_transcripts'] = 'never';
1449 }
1450
1451 // Get old value to check if auto-delete setting changed
1452 $old_options = get_option('mxchat_transcripts_options');
1453 $old_interval = isset($old_options['mxchat_auto_delete_transcripts']) ? $old_options['mxchat_auto_delete_transcripts'] : 'never';
1454
1455 // If the auto-delete setting changed, reschedule the cron job
1456 if ($old_interval !== $sanitized['mxchat_auto_delete_transcripts']) {
1457 $this->schedule_transcript_cleanup($sanitized['mxchat_auto_delete_transcripts']);
1458 }
1459
1460 // Sanitize auto-email transcript settings
1461 $sanitized['mxchat_auto_email_transcript_enabled'] = isset($input['mxchat_auto_email_transcript_enabled']) ? 1 : 0;
1462
1463 $valid_delays = array('15', '30', '60');
1464 if (isset($input['mxchat_auto_email_transcript_delay'])) {
1465 $sanitized['mxchat_auto_email_transcript_delay'] = in_array($input['mxchat_auto_email_transcript_delay'], $valid_delays)
1466 ? $input['mxchat_auto_email_transcript_delay']
1467 : '30';
1468 } else {
1469 $sanitized['mxchat_auto_email_transcript_delay'] = '30';
1470 }
1471
1472 // Sanitize require contact info setting
1473 $sanitized['mxchat_auto_email_transcript_require_contact'] = isset($input['mxchat_auto_email_transcript_require_contact']) ? 1 : 0;
1474
1475 return $sanitized;
1476 }
1477
1478 /**
1479 * Schedule or unschedule the transcript cleanup cron job
1480 */
1481 public function schedule_transcript_cleanup($interval) {
1482 // Clear any existing scheduled event
1483 $timestamp = wp_next_scheduled('mxchat_cleanup_old_transcripts');
1484 if ($timestamp) {
1485 wp_unschedule_event($timestamp, 'mxchat_cleanup_old_transcripts');
1486 }
1487
1488 // Schedule new event if not set to "never"
1489 if ($interval !== 'never') {
1490 // Schedule to run daily at 3 AM
1491 $next_run = strtotime('tomorrow 3:00 AM');
1492 wp_schedule_event($next_run, 'daily', 'mxchat_cleanup_old_transcripts');
1493 }
1494 }
1495
1496 /**
1497 * Delete old transcripts based on the configured interval
1498 */
1499 public function cleanup_old_transcripts() {
1500 $options = get_option('mxchat_transcripts_options', array());
1501 $interval = isset($options['mxchat_auto_delete_transcripts']) ? $options['mxchat_auto_delete_transcripts'] : 'never';
1502
1503 // If set to never, don't delete anything
1504 if ($interval === 'never') {
1505 return;
1506 }
1507
1508 // Calculate the cutoff date
1509 $days = 0;
1510 switch ($interval) {
1511 case '1week':
1512 $days = 7;
1513 break;
1514 case '2weeks':
1515 $days = 14;
1516 break;
1517 case '1month':
1518 $days = 30;
1519 break;
1520 default:
1521 return; // Invalid interval, don't delete anything
1522 }
1523
1524 global $wpdb;
1525 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1526
1527 // Calculate the cutoff timestamp
1528 $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
1529
1530 // Delete transcripts older than the cutoff date
1531 $deleted = $wpdb->query(
1532 $wpdb->prepare(
1533 "DELETE FROM {$table_name} WHERE created_at < %s",
1534 $cutoff_date
1535 )
1536 );
1537
1538 // Log the cleanup action
1539 if ($deleted !== false && $deleted > 0) {
1540 error_log(sprintf('MXChat: Auto-deleted %d transcripts older than %d days', $deleted, $days));
1541 }
1542 }
1543
1544 public function export_chat_transcripts() {
1545 if (!current_user_can('manage_options')) {
1546 wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'mxchat'));
1547 }
1548
1549 check_ajax_referer('mxchat_export_transcripts', 'security');
1550
1551 global $wpdb;
1552 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1553
1554 // Get all transcripts ordered by session and timestamp
1555 $results = $wpdb->get_results(
1556 "SELECT session_id, user_email, user_identifier, role, message, timestamp
1557 FROM {$table_name}
1558 ORDER BY session_id, timestamp ASC"
1559 );
1560
1561 if (empty($results)) {
1562 wp_send_json_error(array('message' => 'No transcripts found.'));
1563 wp_die();
1564 }
1565
1566 // Set headers for CSV download
1567 header('Content-Type: text/csv');
1568 header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
1569 header('Pragma: no-cache');
1570 header('Expires: 0');
1571
1572 // Create output stream
1573 $output = fopen('php://output', 'w');
1574
1575 // Add UTF-8 BOM for proper Excel encoding
1576 fputs($output, "\xEF\xBB\xBF");
1577
1578 // Add CSV headers
1579 fputcsv($output, array(
1580 'Session ID',
1581 'Email',
1582 'User Identifier',
1583 'Role',
1584 'Message',
1585 'Timestamp'
1586 ));
1587
1588 // Add data rows
1589 foreach ($results as $row) {
1590 fputcsv($output, array(
1591 $row->session_id,
1592 $row->user_email,
1593 $row->user_identifier,
1594 $row->role,
1595 $row->message,
1596 $row->timestamp
1597 ));
1598 }
1599
1600 fclose($output);
1601 wp_die();
1602 }
1603
1604
1605 public function mxchat_fetch_chat_history() {
1606 global $wpdb;
1607 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
1608 $url_clicks_table = $wpdb->prefix . 'mxchat_url_clicks';
1609
1610 if (!current_user_can('manage_options')) {
1611 wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
1612 }
1613
1614 $page = isset($_POST['page']) ? absint($_POST['page']) : 1;
1615 $per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
1616 $offset = ($page - 1) * $per_page;
1617 $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
1618
1619 $search_condition = '';
1620 $search_params = [];
1621
1622 if (!empty($search)) {
1623 $search_condition = "WHERE (
1624 session_id LIKE %s
1625 OR user_email LIKE %s
1626 OR user_name LIKE %s
1627 OR user_identifier LIKE %s
1628 OR message LIKE %s
1629 )";
1630 $search_params = array_fill(0, 5, '%' . $wpdb->esc_like($search) . '%');
1631 }
1632
1633 $count_query = !empty($search)
1634 ? $wpdb->prepare("SELECT COUNT(DISTINCT session_id) FROM {$table_name} {$search_condition}", $search_params)
1635 : "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
1636
1637 $total_sessions = $wpdb->get_var($count_query);
1638
1639 $session_query = !empty($search)
1640 ? $wpdb->prepare(
1641 "SELECT DISTINCT t.session_id FROM {$table_name} t {$search_condition}
1642 GROUP BY t.session_id ORDER BY MAX(t.timestamp) DESC LIMIT %d OFFSET %d",
1643 array_merge($search_params, [$per_page, $offset])
1644 )
1645 : $wpdb->prepare(
1646 "SELECT DISTINCT session_id FROM {$table_name}
1647 GROUP BY session_id ORDER BY MAX(timestamp) DESC LIMIT %d OFFSET %d",
1648 $per_page, $offset
1649 );
1650
1651 $session_ids = $wpdb->get_col($session_query);
1652
1653 if (empty($session_ids)) {
1654 ob_start();
1655 echo '<div class="mxchat-no-results">';
1656 echo esc_html__('No chat history found.', 'mxchat');
1657 if (!empty($search)) {
1658 echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
1659 }
1660 echo '</div>';
1661 wp_send_json([
1662 'html' => ob_get_clean(),
1663 'page' => $page,
1664 'total_pages' => 0,
1665 'total_sessions' => 0
1666 ]);
1667 wp_die();
1668 }
1669
1670 $url_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$url_clicks_table'") === $url_clicks_table;
1671 $originating_columns_exist = !empty($wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'originating_page_url'"));
1672
1673 ob_start();
1674 echo '<div class="mxchat-transcript">';
1675
1676 foreach ($session_ids as $session_id) {
1677 $session_data = $originating_columns_exist
1678 ? $wpdb->get_row($wpdb->prepare(
1679 "SELECT user_email, user_name, user_identifier, originating_page_url, originating_page_title, timestamp
1680 FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC LIMIT 1",
1681 $session_id
1682 ))
1683 : $wpdb->get_row($wpdb->prepare(
1684 "SELECT user_email, user_name, user_identifier, timestamp FROM {$table_name}
1685 WHERE session_id = %s LIMIT 1",
1686 $session_id
1687 ));
1688
1689 $clicked_urls = [];
1690 if ($url_table_exists) {
1691 $url_clicks = $wpdb->get_results(
1692 $wpdb->prepare(
1693 "SELECT DISTINCT clicked_url FROM {$url_clicks_table}
1694 WHERE session_id = %s ORDER BY click_timestamp ASC",
1695 $session_id
1696 )
1697 );
1698 foreach ($url_clicks as $click) {
1699 $clicked_urls[] = $click->clicked_url;
1700 }
1701 }
1702
1703 $messages = $wpdb->get_results(
1704 $wpdb->prepare(
1705 "SELECT * FROM {$table_name}
1706 WHERE session_id = %s ORDER BY timestamp ASC",
1707 $session_id
1708 )
1709 );
1710
1711 $latest_timestamp = !empty($messages) ? end($messages)->timestamp : $session_data->timestamp;
1712 $session_time = wp_date('M j, g:ia', strtotime($latest_timestamp . ' UTC'));
1713
1714 $user_email = !empty($session_data->user_email) ? $session_data->user_email : '';
1715 $user_name = !empty($session_data->user_name) ? $session_data->user_name : ''; // NEW
1716 $user_identifier = !empty($session_data->user_identifier) ? $session_data->user_identifier : 'Guest';
1717
1718 // Build user display with name if available
1719 if (!empty($user_email)) {
1720 $user_display = $user_email;
1721 if (!empty($user_name)) {
1722 $user_display .= ' (' . $user_name . ')';
1723 }
1724 } else {
1725 $user_display = $user_identifier;
1726 }
1727 echo '<div class="mxchat-session">';
1728 echo '<div class="mxchat-session-header" data-session-id="' . esc_attr($session_id) . '">';
1729 echo '<div class="mxchat-user-row">' . esc_html($user_display) . '</div>';
1730
1731 if ($originating_columns_exist && !empty($session_data->originating_page_url)) {
1732 $page_title = !empty($session_data->originating_page_title) ? $session_data->originating_page_title : $session_data->originating_page_url;
1733
1734 echo '<div class="mxchat-header-row flex-between">';
1735 echo '<div class="mxchat-main-info">';
1736 echo '<a href="' . esc_url($session_data->originating_page_url) . '" target="_blank" class="mxchat-page-link">';
1737 echo esc_html($page_title);
1738 echo '</a>';
1739 echo '<span class="mxchat-session-time">' . esc_html($session_time) . '</span>';
1740 echo '</div>';
1741 echo '</div>';
1742 }
1743
1744 if (!empty($clicked_urls)) {
1745 echo '<div class="mxchat-links-row">';
1746 echo '<span class="mxchat-label">Clicked:</span> ';
1747 $link_count = 0;
1748 foreach ($clicked_urls as $url) {
1749 if ($link_count > 0) echo ', ';
1750 echo '<a href="' . esc_url($url) . '" target="_blank" class="mxchat-link-item">' . esc_html(parse_url($url, PHP_URL_HOST)) . '</a>';
1751 $link_count++;
1752 if ($link_count >= 3) {
1753 if (count($clicked_urls) > 3) {
1754 echo ' +' . (count($clicked_urls) - 3) . ' more';
1755 }
1756 break;
1757 }
1758 }
1759 echo '</div>';
1760 }
1761
1762 echo '</div>'; // end session-header
1763 echo '<div class="mxchat-messages">';
1764
1765 foreach ($messages as $transcript) {
1766 $formatted_timestamp = wp_date('F j, Y g:i a', strtotime($transcript->timestamp . ' UTC'));
1767
1768 switch ($transcript->role) {
1769 case 'assistant':
1770 case 'bot':
1771 $message_class = 'bot-message';
1772 $display_role = esc_html__('Chatbot', 'mxchat');
1773 break;
1774 case 'user':
1775 $message_class = 'user-message';
1776 $display_role = !empty($transcript->user_identifier)
1777 ? sanitize_text_field($transcript->user_identifier)
1778 : esc_html__('User', 'mxchat');
1779 break;
1780 case 'agent':
1781 $message_class = 'agent-message';
1782 $display_role = esc_html__('Agent', 'mxchat');
1783 break;
1784 default:
1785 $message_class = 'unknown-message';
1786 $display_role = esc_html__('Unknown', 'mxchat');
1787 }
1788
1789 $message_content = wp_kses(
1790 stripslashes($transcript->message),
1791 [
1792 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1793 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1794 'a' => ['href' => [], 'title' => [], 'target' => []]
1795 ]
1796 );
1797 $message_content = nl2br($message_content);
1798
1799 echo '<div class="mxchat-message ' . esc_attr($message_class) . '">';
1800 echo '<div class="mxchat-message-header">' . esc_html($display_role) . '</div>';
1801 echo '<div class="mxchat-message-content">' . $message_content . '</div>';
1802 echo '<div class="mxchat-timestamp">' . esc_html($formatted_timestamp) . '</div>';
1803 echo '</div>';
1804 }
1805
1806 echo '</div></div>'; // end messages and session
1807 }
1808
1809 echo '</div>'; // end transcript
1810
1811 $total_pages = ceil($total_sessions / $per_page);
1812
1813 echo '<div class="mxchat-pagination">';
1814 echo '<div class="mxchat-pagination-info">';
1815 echo sprintf(
1816 esc_html__('Showing %1$d to %2$d of %3$d sessions', 'mxchat'),
1817 $offset + 1,
1818 min($offset + $per_page, $total_sessions),
1819 $total_sessions
1820 );
1821 echo '</div>';
1822
1823 if ($total_pages > 1) {
1824 echo '<div class="mxchat-pagination-controls">';
1825 if ($page > 1) {
1826 echo '<button class="mxchat-pagination-button" data-page="' . ($page - 1) . '">&laquo; ' . esc_html__('Previous', 'mxchat') . '</button>';
1827 }
1828
1829 $start_page = max(1, $page - 2);
1830 $end_page = min($total_pages, $page + 2);
1831
1832 if ($start_page > 1) {
1833 echo '<button class="mxchat-pagination-button" data-page="1">1</button>';
1834 if ($start_page > 2) {
1835 echo '<span class="mxchat-pagination-ellipsis">...</span>';
1836 }
1837 }
1838
1839 for ($i = $start_page; $i <= $end_page; $i++) {
1840 $class = $i === $page ? 'mxchat-pagination-button active' : 'mxchat-pagination-button';
1841 echo '<button class="' . $class . '" data-page="' . $i . '">' . $i . '</button>';
1842 }
1843
1844 if ($end_page < $total_pages) {
1845 if ($end_page < $total_pages - 1) {
1846 echo '<span class="mxchat-pagination-ellipsis">...</span>';
1847 }
1848 echo '<button class="mxchat-pagination-button" data-page="' . $total_pages . '">' . $total_pages . '</button>';
1849 }
1850
1851 if ($page < $total_pages) {
1852 echo '<button class="mxchat-pagination-button" data-page="' . ($page + 1) . '">' . esc_html__('Next', 'mxchat') . ' &raquo;</button>';
1853 }
1854
1855 echo '</div>';
1856 }
1857
1858 echo '</div>';
1859
1860 wp_send_json([
1861 'html' => ob_get_clean(),
1862 'page' => $page,
1863 'total_pages' => $total_pages,
1864 'total_sessions' => $total_sessions
1865 ]);
1866
1867 wp_die();
1868 }
1869
1870
1871 /**
1872 * ALTERNATIVE: Simpler helper method using string replacement
1873 */
1874 private function highlight_clicked_links($message_content, $clicked_urls) {
1875 if (empty($clicked_urls)) {
1876 return wp_kses(
1877 $message_content,
1878 [
1879 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1880 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1881 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1882 ]
1883 );
1884 }
1885
1886 // First apply standard sanitization
1887 $message_content = wp_kses(
1888 $message_content,
1889 [
1890 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [],
1891 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
1892 'a' => ['href' => [], 'title' => [], 'target' => [], 'class' => []]
1893 ]
1894 );
1895
1896 // Process each clicked URL
1897 foreach ($clicked_urls as $clicked_url) {
1898 // Try multiple patterns to catch different link formats
1899 $patterns = [
1900 // Standard link format
1901 '/<a([^>]*href=["\']' . preg_quote($clicked_url, '/') . '["\'][^>]*)>/i',
1902 // Link with trailing slash
1903 '/<a([^>]*href=["\']' . preg_quote(rtrim($clicked_url, '/'), '/') . '\/?["\'][^>]*)>/i',
1904 // Encoded entities version
1905 '/<a([^>]*href=["\']' . preg_quote(htmlentities($clicked_url), '/') . '["\'][^>]*)>/i',
1906 ];
1907
1908 foreach ($patterns as $pattern) {
1909 if (preg_match($pattern, $message_content)) {
1910 $message_content = preg_replace_callback(
1911 $pattern,
1912 function($matches) {
1913 $full_match = $matches[0];
1914 $attributes = $matches[1];
1915
1916 // Check if it already has the class
1917 if (strpos($full_match, 'mxchat-clicked-link') !== false) {
1918 return $full_match;
1919 }
1920
1921 // Check if class attribute exists
1922 if (preg_match('/class=["\']([^"\']*)["\']/', $attributes, $class_matches)) {
1923 // Add to existing class
1924 $new_attributes = preg_replace(
1925 '/class=["\']([^"\']*)["\']/',
1926 'class="$1 mxchat-clicked-link"',
1927 $attributes
1928 );
1929 } else {
1930 // Add new class attribute
1931 $new_attributes = $attributes . ' class="mxchat-clicked-link"';
1932 }
1933
1934 // Add title if not present
1935 if (strpos($new_attributes, 'title=') === false) {
1936 $new_attributes .= ' title="User clicked this link"';
1937 }
1938
1939 return '<a' . $new_attributes . '>';
1940 },
1941 $message_content
1942 );
1943
1944 // If we found and replaced, break out of the patterns loop
1945 break;
1946 }
1947 }
1948 }
1949
1950 return $message_content;
1951 }
1952
1953 public function mxchat_create_prompts_page() {
1954 //error_log('=== DEBUG: mxchat_create_prompts_page started ===');
1955
1956 global $wpdb;
1957 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
1958
1959 $knowledge_manager = MxChat_Knowledge_Manager::get_instance();
1960 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
1961
1962 // Display success message if all prompts were deleted
1963 if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') {
1964 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '</p></div>';
1965 }
1966
1967 // Set up pagination, search query, and content type filter
1968 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
1969 $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : '';
1970 $content_type_filter = isset($_GET['content_type']) ? sanitize_key($_GET['content_type']) : ''; // ADDED 2.5.6
1971 $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
1972 $per_page = 10;
1973
1974 //error_log('DEBUG: Search query: ' . $search_query);
1975 //error_log('DEBUG: Content type filter: ' . $content_type_filter);
1976 //error_log('DEBUG: Current page: ' . $current_page);
1977 //error_log('DEBUG: Per page: ' . $per_page);
1978
1979 // ================================
1980 // MULTI-BOT CONFIGURATION
1981 // ================================
1982
1983 // Check for multi-bot and set up bot selection
1984 if (class_exists('MxChat_Multi_Bot_Manager')) {
1985 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
1986 $available_bots = $multi_bot_manager->get_available_bots();
1987
1988 // Get saved bot selection (user-specific first, then site-wide default)
1989 $user_id = get_current_user_id();
1990 $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
1991 if (empty($saved_bot_id)) {
1992 $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
1993 }
1994
1995 // Allow URL override but default to saved selection
1996 $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
1997 $multibot_active = true;
1998 } else {
1999 $current_bot_id = 'default';
2000 $multibot_active = false;
2001 }
2002
2003 // ================================
2004 // DATA SOURCE CONFIGURATION
2005 // ================================
2006
2007 // Get bot-specific Pinecone settings
2008 $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($current_bot_id);
2009 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
2010 $pinecone_api_key = $pinecone_options['mxchat_pinecone_api_key'] ?? '';
2011
2012 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
2013 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Has API Key: ' . (!empty($pinecone_api_key) ? 'YES' : 'NO'));
2014 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
2015 //error_log('DEBUG: Bot ' . $current_bot_id . ' - Namespace: ' . ($pinecone_options['mxchat_pinecone_namespace'] ?? 'NOT SET'));
2016
2017 if ($use_pinecone && !empty($pinecone_api_key)) {
2018 //error_log('DEBUG: Using PINECONE data source with bot-specific config');
2019 // PINECONE DATA SOURCE
2020 $data_source = 'pinecone';
2021
2022 // IMPORTANT: Pass the bot-specific $pinecone_options, not default options!
2023 // UPDATED 2.5.6: Added content_type_filter parameter
2024 $records = $pinecone_manager->mxchat_fetch_pinecone_records($pinecone_options, $search_query, $current_page, $per_page, $current_bot_id, $content_type_filter);
2025
2026 // TEMPORARY DEBUG - Add this right after the fetch call
2027 //error_log('=== DEBUG: Bot switching issue ===');
2028 //error_log('Current bot ID: ' . $current_bot_id);
2029 //error_log('Use Pinecone: ' . ($use_pinecone ? 'YES' : 'NO'));
2030 //error_log('Records returned: ' . count($records['data'] ?? []));
2031 //error_log('Total records: ' . ($records['total'] ?? 0));
2032
2033 // Check first few records to see their bot_id
2034 if (!empty($records['data'])) {
2035 foreach (array_slice($records['data'], 0, 3) as $i => $record) {
2036 $record_bot_id = $record->bot_id ?? 'NOT_SET';
2037 //error_log('Record ' . ($i+1) . ' bot_id: ' . $record_bot_id . ', content preview: ' . substr($record->article_content ?? '', 0, 30) . '...');
2038 }
2039 }
2040 //error_log('=== END DEBUG ===');
2041
2042 $total_records = $records['total'] ?? 0;
2043 $prompts = $records['data'] ?? array();
2044 $total_in_database = $records['total_in_database'] ?? 0;
2045 $showing_recent_only = $records['showing_recent_only'] ?? false;
2046
2047 $total_pages = ceil($total_records / $per_page);
2048
2049 } else {
2050 //error_log('DEBUG: Using WORDPRESS DB data source');
2051 // WORDPRESS DB DATA SOURCE (your existing logic)
2052 $data_source = 'wordpress';
2053
2054 // Initialize these variables for WordPress DB
2055 $total_in_database = 0;
2056 $showing_recent_only = false;
2057
2058 $offset = ($current_page - 1) * $per_page;
2059
2060 // UPDATED 2.5.6: Build WHERE clause for search and content type filtering
2061 $where_clauses = array();
2062 $where_values = array();
2063
2064 if ($search_query) {
2065 $where_clauses[] = "article_content LIKE %s";
2066 $where_values[] = '%' . $wpdb->esc_like($search_query) . '%';
2067 }
2068
2069 if ($content_type_filter) {
2070 $where_clauses[] = "content_type = %s";
2071 $where_values[] = $content_type_filter;
2072 }
2073
2074 $sql_where = "";
2075 if (!empty($where_clauses)) {
2076 $sql_where = "WHERE " . implode(" AND ", $where_clauses);
2077 }
2078
2079 // Retrieve total number of prompts, considering filters
2080 if (!empty($where_values)) {
2081 $count_query = $wpdb->prepare("SELECT COUNT(*) FROM {$table_name} {$sql_where}", $where_values);
2082 } else {
2083 $count_query = "SELECT COUNT(*) FROM {$table_name} {$sql_where}";
2084 }
2085 $total_records = $wpdb->get_var($count_query);
2086 $total_pages = ceil($total_records / $per_page);
2087
2088 // Retrieve prompts from the database
2089 $limit_offset_values = array_merge($where_values, array($per_page, $offset));
2090 $prompts_query = $wpdb->prepare(
2091 "SELECT * FROM {$table_name} {$sql_where} ORDER BY timestamp DESC LIMIT %d OFFSET %d",
2092 ...$limit_offset_values
2093 );
2094
2095 $prompts = $wpdb->get_results($prompts_query);
2096 }
2097
2098 // ================================
2099 // PAGINATION GENERATION
2100 // ================================
2101
2102 // Generate pagination links
2103 if ($total_pages > 1) {
2104 // Build base URL with necessary parameters
2105 $base_url = add_query_arg('paged', '%#%');
2106
2107 // Preserve bot_id parameter if multi-bot is active
2108 if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') {
2109 $base_url = add_query_arg('bot_id', $current_bot_id, $base_url);
2110 }
2111
2112 // Preserve search query if present
2113 if ($search_query) {
2114 $base_url = add_query_arg('search', $search_query, $base_url);
2115 }
2116
2117 // UPDATED 2.5.6: Preserve content type filter if present
2118 if ($content_type_filter) {
2119 $base_url = add_query_arg('content_type', $content_type_filter, $base_url);
2120 }
2121
2122 $page_links = paginate_links(array(
2123 'base' => $base_url,
2124 'format' => '',
2125 'prev_text' => __('&laquo; Previous', 'mxchat'),
2126 'next_text' => __('Next &raquo;', 'mxchat'),
2127 'total' => $total_pages,
2128 'current' => $current_page,
2129 'type' => 'plain',
2130 ));
2131 } else {
2132 $page_links = '';
2133 }
2134
2135 // ================================
2136 // PROCESSING STATUS RETRIEVAL
2137 // ================================
2138
2139 // Retrieve processing statuses
2140 $pdf_url = get_transient('mxchat_last_pdf_url');
2141 $sitemap_url = get_transient('mxchat_last_sitemap_url');
2142 $pdf_status = $pdf_url ? $knowledge_manager->mxchat_get_pdf_processing_status($pdf_url) : false;
2143 $sitemap_status = $sitemap_url ? $knowledge_manager->mxchat_get_sitemap_processing_status($sitemap_url) : false;
2144
2145 $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error'))
2146 || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error'));
2147
2148 //error_log('=== DEBUG: mxchat_create_prompts_page data preparation completed ===');
2149
2150 ?>
2151
2152 <div class="wrap mxchat-wrapper">
2153 <!-- Hero Section -->
2154 <div class="mxchat-hero">
2155 <h1 class="mxchat-main-title">
2156 <span class="mxchat-gradient-text">Knowledge Base</span> Manager
2157 </h1>
2158 <p class="mxchat-hero-subtitle">
2159 <?php esc_html_e('Enhance your AI chatbot with custom knowledge. Import, manage, and organize your content to keep responses accurate and relevant.', 'mxchat'); ?>
2160 </p>
2161 </div>
2162
2163 <div class="mxchat-content">
2164
2165 <!-- Multi-Bot Selector (only shows if multi-bot addon is active) -->
2166 <?php if (class_exists('MxChat_Multi_Bot_Manager')) :
2167 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
2168 $available_bots = $multi_bot_manager->get_available_bots();
2169
2170 // Get saved bot selection (user-specific first, then site-wide default)
2171 $user_id = get_current_user_id();
2172 $saved_bot_id = get_user_meta($user_id, 'mxchat_selected_knowledge_bot', true);
2173 if (empty($saved_bot_id)) {
2174 $saved_bot_id = get_option('mxchat_current_knowledge_bot', 'default');
2175 }
2176
2177 // Allow URL override but default to saved selection
2178 $current_bot_id = isset($_GET['bot_id']) ? sanitize_key($_GET['bot_id']) : $saved_bot_id;
2179 ?>
2180 <div class="mxchat-card" style="margin-bottom: 20px;">
2181 <div class="mxchat-bot-selector-wrapper" style="display: flex; align-items: center; gap: 15px;">
2182 <label for="mxchat-bot-selector" style="font-weight: 600;">
2183 <?php esc_html_e('Select Bot Database:', 'mxchat'); ?>
2184 </label>
2185 <select id="mxchat-bot-selector" class="mxchat-bot-selector" style="min-width: 200px;">
2186 <?php foreach ($available_bots as $bot_id => $bot_name) : ?>
2187 <option value="<?php echo esc_attr($bot_id); ?>" <?php selected($current_bot_id, $bot_id); ?>>
2188 <?php echo esc_html($bot_name); ?>
2189 </option>
2190 <?php endforeach; ?>
2191 </select>
2192 <span class="mxchat-bot-info" style="color: #666; font-size: 13px;">
2193 <?php esc_html_e('Content will be added to the selected bot\'s knowledge base', 'mxchat'); ?>
2194 </span>
2195 <span id="mxchat-bot-save-status" style="display: none; color: #00a32a; font-size: 13px;">
2196 ✓ <?php esc_html_e('Saved', 'mxchat'); ?>
2197 </span>
2198 </div>
2199 </div>
2200
2201 <?php
2202 // Set a flag so we know multi-bot is active throughout the page
2203 $multibot_active = true;
2204 else:
2205 $current_bot_id = 'default';
2206 $multibot_active = false;
2207 endif;
2208 ?>
2209
2210 <!-- Tab Navigation -->
2211 <div class="mxchat-kb-tabs-nav">
2212 <button class="mxchat-kb-tab-button active" data-tab="import">
2213 <?php esc_html_e('Knowledge Import', 'mxchat'); ?>
2214 </button>
2215 <button class="mxchat-kb-tab-button" data-tab="sync">
2216 <?php esc_html_e('Settings', 'mxchat'); ?>
2217 </button>
2218 <button class="mxchat-kb-tab-button" data-tab="pinecone">
2219 <?php esc_html_e('Pinecone Settings', 'mxchat'); ?>
2220 </button>
2221 </div>
2222
2223 <div class="mxchat-kb-tabs-content">
2224 <div id="mxchat-kb-tab-import" class="mxchat-kb-tab-content active">
2225 <!-- Import Options Card -->
2226 <div class="mxchat-card">
2227
2228 <h2><?php esc_html_e('Knowledge Import Settings', 'mxchat'); ?></h2>
2229
2230 <?php
2231 // Check if the appropriate embedding API key exists
2232 $embedding_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
2233 $has_openai_key = !empty($this->options['api_key']);
2234 $has_voyage_key = !empty($this->options['voyage_api_key']);
2235 $has_gemini_key = !empty($this->options['gemini_api_key']);
2236
2237 // Determine if they have the needed API key for their selected embedding model
2238 $has_required_key = false;
2239 $required_key_type = '';
2240
2241 if (strpos($embedding_model, 'text-embedding-') !== false && $has_openai_key) {
2242 $has_required_key = true;
2243 $required_key_type = 'OpenAI';
2244 } elseif (strpos($embedding_model, 'voyage-') !== false && $has_voyage_key) {
2245 $has_required_key = true;
2246 $required_key_type = 'Voyage AI';
2247 } elseif (strpos($embedding_model, 'gemini-embedding-') !== false && $has_gemini_key) {
2248 $has_required_key = true;
2249 $required_key_type = 'Google Gemini';
2250 } elseif (strpos($embedding_model, 'text-embedding-') !== false) {
2251 $required_key_type = 'OpenAI';
2252 } elseif (strpos($embedding_model, 'voyage-') !== false) {
2253 $required_key_type = 'Voyage AI';
2254 } elseif (strpos($embedding_model, 'gemini-embedding-') !== false) {
2255 $required_key_type = 'Google Gemini';
2256 }
2257 ?>
2258
2259 <div class="mxchat-knowledge-warning <?php echo $has_required_key ? 'success' : 'warning'; ?>">
2260 <?php if ($has_required_key): ?>
2261 <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>
2262 <?php else: ?>
2263 <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>
2264 <?php endif; ?>
2265 </div>
2266
2267 <!-- Import Options Section -->
2268 <div class="mxchat-import-section">
2269 <h3><?php esc_html_e('Import Options', 'mxchat'); ?></h3>
2270
2271 <!-- Import Options Grid -->
2272 <div class="mxchat-import-options">
2273 <!-- WordPress Import Option -->
2274 <button type="button" id="mxchat-open-content-selector" class="mxchat-import-box mxchat-import-wordpress" data-option="wordpress">
2275 <div class="mxchat-import-icon">
2276 <span class="dashicons dashicons-wordpress"></span>
2277 </div>
2278 <div class="mxchat-import-content">
2279 <h4><?php esc_html_e('WordPress Content', 'mxchat'); ?></h4>
2280 <p><?php esc_html_e('Import specific posts and pages to your knowledge base.', 'mxchat'); ?></p>
2281 </div>
2282 <div class="mxchat-recommended-tag"><?php esc_html_e('Recommended', 'mxchat'); ?></div>
2283 </button>
2284
2285 <!-- Sitemap Import Option -->
2286 <button type="button" class="mxchat-import-box" data-option="sitemap" data-placeholder="<?php esc_attr_e('Enter sitemap URL here', 'mxchat'); ?>" data-type="sitemap">
2287 <div class="mxchat-import-icon">
2288 <span class="dashicons dashicons-admin-site-alt"></span>
2289 </div>
2290 <div class="mxchat-import-content">
2291 <h4><?php esc_html_e('Sitemap Import', 'mxchat'); ?></h4>
2292 <p><?php esc_html_e('Use a content-specific sub-sitemap, not the sitemap index.', 'mxchat'); ?></p>
2293 </div>
2294 </button>
2295
2296 <!-- Direct URL Import Option -->
2297 <button type="button" class="mxchat-import-box" data-option="url" data-placeholder="<?php esc_attr_e('Enter webpage URL here', 'mxchat'); ?>" data-type="url">
2298 <div class="mxchat-import-icon">
2299 <span class="dashicons dashicons-admin-links"></span>
2300 </div>
2301 <div class="mxchat-import-content">
2302 <h4><?php esc_html_e('Direct URL', 'mxchat'); ?></h4>
2303 <p><?php esc_html_e('Import content from any webpage.', 'mxchat'); ?></p>
2304 </div>
2305 </button>
2306
2307 <!-- Direct Content Import Option -->
2308 <button type="button" class="mxchat-import-box" data-option="content" data-type="content">
2309 <div class="mxchat-import-icon">
2310 <span class="dashicons dashicons-editor-paste-text"></span>
2311 </div>
2312 <div class="mxchat-import-content">
2313 <h4><?php esc_html_e('Direct Content', 'mxchat'); ?></h4>
2314 <p><?php esc_html_e('Submit content to be vectorized.', 'mxchat'); ?></p>
2315 </div>
2316 </button>
2317
2318 <!-- PDF Import Option -->
2319 <button type="button" class="mxchat-import-box" data-option="pdf" data-placeholder="<?php esc_attr_e('Enter PDF URL here', 'mxchat'); ?>" data-type="pdf">
2320 <div class="mxchat-import-icon">
2321 <span class="dashicons dashicons-media-document"></span>
2322 </div>
2323 <div class="mxchat-import-content">
2324 <h4><?php esc_html_e('PDF Import', 'mxchat'); ?></h4>
2325 <p><?php esc_html_e('Import knowledge from PDF documents.', 'mxchat'); ?></p>
2326 </div>
2327 </button>
2328 </div>
2329
2330 <!-- Input Areas for URL and Content -->
2331 <div class="mxchat-import-input-area" id="mxchat-url-input-area" style="display: none;">
2332 <?php if (!$is_processing) : ?>
2333 <form id="mxchat-url-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_sitemap')); ?>">
2334 <?php wp_nonce_field('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); ?>
2335 <input type="hidden" name="import_type" id="import_type" value="url">
2336 <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
2337 <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
2338 <?php endif; ?>
2339 <div class="mxchat-url-input-group">
2340 <input type="url"
2341 name="sitemap_url"
2342 id="sitemap_url"
2343 placeholder="<?php esc_attr_e('Enter URL here', 'mxchat'); ?>"
2344 required />
2345 <button type="submit"
2346 name="submit_sitemap"
2347 class="mxchat-button-primary">
2348 <?php esc_html_e('Import', 'mxchat'); ?>
2349 </button>
2350 </div>
2351 <p class="mxchat-url-description" id="url-description-text"></p>
2352 </form>
2353 <?php endif; ?>
2354 </div>
2355
2356 <div class="mxchat-import-input-area" id="mxchat-content-input-area" style="display: none;">
2357 <?php if (!$is_processing) : ?>
2358 <form id="mxchat-content-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_submit_content')); ?>">
2359 <?php wp_nonce_field('mxchat_submit_content_action', 'mxchat_submit_content_nonce'); ?>
2360 <?php if ($multibot_active && $current_bot_id !== 'default') : ?>
2361 <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>">
2362 <?php endif; ?>
2363 <div class="mxchat-form-group">
2364 <textarea
2365 name="article_content"
2366 id="article_content"
2367 placeholder="<?php esc_attr_e('Enter your content here...', 'mxchat'); ?>"
2368 required
2369 rows="6"
2370 ></textarea>
2371 </div>
2372 <div class="mxchat-form-group">
2373 <input type="url"
2374 name="article_url"
2375 id="article_url"
2376 placeholder="<?php esc_attr_e('Enter source URL (Optional)', 'mxchat'); ?>">
2377 <div class="mxchat-url-helper" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
2378 <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: #666; cursor: pointer;">
2379 <input type="checkbox" id="mxchat-unique-url-toggle" style="margin: 0;">
2380 <?php esc_html_e('Generate unique URL for duplicate content', 'mxchat'); ?>
2381 </label>
2382 <button type="button"
2383 id="mxchat-generate-unique-url"
2384 class="mxchat-button-secondary"
2385 style="padding: 4px 12px; font-size: 12px; display: none;">
2386 <span class="dashicons dashicons-randomize" style="font-size: 14px; margin-top: 2px;"></span>
2387 <?php esc_html_e('Generate Unique', 'mxchat'); ?>
2388 </button>
2389 </div>
2390 <p class="description" style="margin-top: 8px; font-size: 12px; color: #666;">
2391 <?php esc_html_e('Enable this option to submit multiple entries with the same base URL. A unique reference will be appended (e.g., ?ref=1731234567890).', 'mxchat'); ?>
2392 </p>
2393 </div>
2394 <button type="submit"
2395 name="submit_content"
2396 class="mxchat-button-primary">
2397 <?php esc_html_e('Import Content', 'mxchat'); ?>
2398 </button>
2399 </form>
2400 <?php endif; ?>
2401 </div>
2402 </div>
2403
2404 <!-- PDF Processing Status - ONLY PROCESSING -->
2405 <?php if ($pdf_status && $pdf_status['status'] === 'processing') : ?>
2406 <div class="mxchat-status-card" data-card-type="pdf">
2407 <div class="mxchat-status-header">
2408 <h4><?php esc_html_e('PDF Processing Status', 'mxchat'); ?></h4>
2409
2410 <!-- Processing controls -->
2411 <div class="mxchat-processing-controls">
2412 <form method="post" class="mxchat-stop-form"
2413 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2414 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2415 <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2416 <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2417 </button>
2418 </form>
2419
2420 <button type="button" class="mxchat-manual-batch-btn"
2421 data-process-type="pdf"
2422 data-url="<?php echo esc_attr(get_transient('mxchat_last_pdf_url')); ?>">
2423 <?php esc_html_e('Process Batch', 'mxchat'); ?>
2424 </button>
2425 </div>
2426 </div>
2427
2428 <div class="mxchat-progress-bar">
2429 <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($pdf_status['percentage']); ?>%"></div>
2430 </div>
2431
2432 <div class="mxchat-status-details">
2433 <p><?php printf(
2434 esc_html__('Progress: %1$d of %2$d pages (%3$d%%)', 'mxchat'),
2435 absint($pdf_status['processed_pages']),
2436 absint($pdf_status['total_pages']),
2437 absint($pdf_status['percentage'])
2438 ); ?></p>
2439
2440 <?php if (!empty($pdf_status['failed_pages']) && $pdf_status['failed_pages'] > 0) : ?>
2441 <p><strong><?php esc_html_e('Failed pages:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['failed_pages']); ?></p>
2442 <?php endif; ?>
2443
2444 <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($pdf_status['status'])); ?></p>
2445 <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($pdf_status['last_update']); ?></p>
2446 </div>
2447 </div>
2448 <?php endif; ?>
2449
2450 <!-- Sitemap Processing Status - ONLY PROCESSING -->
2451 <?php if ($sitemap_status && $sitemap_status['status'] === 'processing') : ?>
2452 <div class="mxchat-status-card" data-card-type="sitemap">
2453 <div class="mxchat-status-header">
2454 <h4><?php esc_html_e('Sitemap Processing Status', 'mxchat'); ?></h4>
2455
2456 <!-- Processing controls -->
2457 <div class="mxchat-processing-controls">
2458 <form method="post" class="mxchat-stop-form"
2459 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_stop_processing')); ?>">
2460 <?php wp_nonce_field('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); ?>
2461 <button type="submit" name="stop_processing" class="mxchat-button-secondary">
2462 <?php esc_html_e('Stop Processing', 'mxchat'); ?>
2463 </button>
2464 </form>
2465
2466 <button type="button" class="mxchat-manual-batch-btn"
2467 data-process-type="sitemap"
2468 data-url="<?php echo esc_attr(get_transient('mxchat_last_sitemap_url')); ?>">
2469 <?php esc_html_e('Process Batch', 'mxchat'); ?>
2470 </button>
2471 </div>
2472 </div>
2473
2474 <div class="mxchat-progress-bar">
2475 <div class="mxchat-progress-fill" style="width: <?php echo esc_attr($sitemap_status['percentage']); ?>%"></div>
2476 </div>
2477
2478 <div class="mxchat-status-details">
2479 <p><?php printf(
2480 esc_html__('Progress: %1$d of %2$d URLs (%3$d%%)', 'mxchat'),
2481 absint($sitemap_status['processed_urls']),
2482 absint($sitemap_status['total_urls']),
2483 absint($sitemap_status['percentage'])
2484 ); ?></p>
2485
2486 <?php if (!empty($sitemap_status['failed_urls']) && $sitemap_status['failed_urls'] > 0) : ?>
2487 <p><strong><?php esc_html_e('Failed URLs:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['failed_urls']); ?></p>
2488 <?php endif; ?>
2489
2490 <p><strong><?php esc_html_e('Status:', 'mxchat'); ?></strong> <?php echo esc_html(ucfirst($sitemap_status['status'])); ?></p>
2491 <p><strong><?php esc_html_e('Last update:', 'mxchat'); ?></strong> <?php echo esc_html($sitemap_status['last_update']); ?></p>
2492
2493 <?php if (!empty($sitemap_status['error']) || !empty($sitemap_status['last_error'])) : ?>
2494 <div class="mxchat-error-notice">
2495 <?php if (!empty($sitemap_status['error'])) : ?>
2496 <p class="error"><?php echo esc_html($sitemap_status['error']); ?></p>
2497 <?php endif; ?>
2498 <?php if (!empty($sitemap_status['last_error'])) : ?>
2499 <p class="last-error"><?php echo esc_html__('Last error:', 'mxchat') . ' ' . esc_html($sitemap_status['last_error']); ?></p>
2500 <?php endif; ?>
2501 </div>
2502 <?php endif; ?>
2503 </div>
2504 </div>
2505 <?php endif; ?>
2506
2507 <!-- Single URL Submission Status -->
2508 <?php
2509 // Get single URL status
2510 $single_url_status = $this->knowledge_manager->mxchat_get_single_url_status();
2511 $is_active_processing =
2512 ($sitemap_status && $sitemap_status['status'] === 'processing') ||
2513 ($pdf_status && $pdf_status['status'] === 'processing');
2514 ?>
2515
2516 <div id="mxchat-single-url-status-container" <?php echo $is_active_processing ? 'style="display:none;"' : ''; ?>>
2517 <?php if ($single_url_status && !$is_active_processing) : ?>
2518 <div class="mxchat-status-card">
2519 <div class="mxchat-status-header">
2520 <h4><?php esc_html_e('Last URL Submission', 'mxchat'); ?></h4>
2521 <?php if ($single_url_status['status'] === 'failed') : ?>
2522 <span class="mxchat-status-badge mxchat-status-failed"><?php esc_html_e('Failed', 'mxchat'); ?></span>
2523 <?php else : ?>
2524 <span class="mxchat-status-badge mxchat-status-success"><?php esc_html_e('Success', 'mxchat'); ?></span>
2525 <?php endif; ?>
2526 </div>
2527 <div class="mxchat-status-details">
2528 <p><strong><?php esc_html_e('URL:', 'mxchat'); ?></strong>
2529 <a href="<?php echo esc_url($single_url_status['url']); ?>" target="_blank">
2530 <?php echo esc_html(strlen($single_url_status['url']) > 60 ? substr($single_url_status['url'], 0, 57) . '...' : $single_url_status['url']); ?>
2531 </a>
2532 </p>
2533 <p><strong><?php esc_html_e('Submitted:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['human_time']); ?></p>
2534
2535 <?php if ($single_url_status['status'] === 'failed' && !empty($single_url_status['error'])) : ?>
2536 <div class="mxchat-error-notice">
2537 <p class="error"><?php echo esc_html($single_url_status['error']); ?></p>
2538 </div>
2539 <?php endif; ?>
2540
2541 <?php if ($single_url_status['status'] === 'complete') : ?>
2542 <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>
2543 <p><strong><?php esc_html_e('Embedding Dimensions:', 'mxchat'); ?></strong> <?php echo esc_html($single_url_status['embedding_dimensions']); ?></p>
2544 <?php endif; ?>
2545 </div>
2546 </div>
2547 <?php endif; ?>
2548 </div>
2549
2550 <!-- Completed Status Cards - Handles completed PDF/Sitemap processing -->
2551 <?php
2552 echo $knowledge_manager->mxchat_render_completed_status_cards();
2553 ?>
2554
2555 </div>
2556
2557 <!-- Knowledge Base Table Card -->
2558 <div class="mxchat-card">
2559 <div class="mxchat-card-header">
2560 <h2>
2561 <?php esc_html_e('Knowledge Base', 'mxchat'); ?>
2562 <span class="mxchat-record-count">
2563 <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2564 (<?php echo esc_html($total_records); ?> of <?php echo esc_html(number_format($total_in_database)); ?> total - recent entries only)
2565 <?php else: ?>
2566 (<?php echo esc_html($total_records); ?>)
2567 <?php endif; ?>
2568 </span>
2569 <!-- Data source badge -->
2570 <?php if ($use_pinecone) : ?>
2571 <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;">
2572 <span class="dashicons dashicons-cloud"></span>
2573 <?php esc_html_e('Pinecone', 'mxchat'); ?>
2574 </span>
2575 <?php else : ?>
2576 <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;">
2577 <span class="dashicons dashicons-database-view"></span>
2578 <?php esc_html_e('WordPress DB', 'mxchat'); ?>
2579 </span>
2580 <?php endif; ?>
2581 </h2>
2582 <div class="mxchat-header-actions">
2583 <!-- Search -->
2584 <form method="get" id="knowledge-search" class="mxchat-search-form">
2585 <?php wp_nonce_field('mxchat_prompts_search_nonce'); ?>
2586 <input type="hidden" name="page" value="mxchat-prompts" />
2587 <?php if ($multibot_active && !empty($current_bot_id) && $current_bot_id !== 'default') : ?>
2588 <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2589 <?php endif; ?>
2590 <div class="mxchat-search-group">
2591 <span class="dashicons dashicons-search"></span>
2592 <input type="text"
2593 name="search"
2594 placeholder="<?php esc_attr_e('Search Knowledge', 'mxchat'); ?>"
2595 value="<?php echo esc_attr($search_query); ?>" />
2596 </div>
2597
2598 <!-- ADDED 2.5.6: Content Type Filter -->
2599 <div class="mxchat-filter-group">
2600 <select name="content_type" id="mxchat-content-type-filter" onchange="this.form.submit()">
2601 <option value=""><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2602 <option value="post" <?php selected($content_type_filter, 'post'); ?>><?php esc_html_e('Posts', 'mxchat'); ?></option>
2603 <option value="page" <?php selected($content_type_filter, 'page'); ?>><?php esc_html_e('Pages', 'mxchat'); ?></option>
2604 <option value="product" <?php selected($content_type_filter, 'product'); ?>><?php esc_html_e('Products', 'mxchat'); ?></option>
2605 <option value="pdf" <?php selected($content_type_filter, 'pdf'); ?>><?php esc_html_e('PDFs', 'mxchat'); ?></option>
2606 <option value="url" <?php selected($content_type_filter, 'url'); ?>><?php esc_html_e('URLs', 'mxchat'); ?></option>
2607 <option value="content" <?php selected($content_type_filter, 'content'); ?>><?php esc_html_e('Manual Content', 'mxchat'); ?></option>
2608 </select>
2609 </div>
2610 </form>
2611
2612 <form method="post"
2613 action="<?php echo esc_url(admin_url('admin-post.php?action=mxchat_delete_all_prompts')); ?>"
2614 class="mxchat-delete-form"
2615 onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete all knowledge? This action cannot be undone.', 'mxchat'); ?>');">
2616 <?php wp_nonce_field('mxchat_delete_all_prompts_action', 'mxchat_delete_all_prompts_nonce'); ?>
2617 <input type="hidden" name="data_source" value="<?php echo esc_attr($data_source); ?>" />
2618 <!-- Always include bot_id, even if it's 'default' -->
2619 <input type="hidden" name="bot_id" value="<?php echo esc_attr($current_bot_id); ?>" />
2620 <button type="submit" name="delete_all_prompts" class="mxchat-button-danger">
2621 <span class="dashicons dashicons-trash"></span>
2622 <?php esc_html_e('Delete All', 'mxchat'); ?>
2623 </button>
2624 </form>
2625 </div>
2626 </div>
2627
2628 <!-- Recent Entries Info Banner -->
2629 <?php if ($showing_recent_only && $total_in_database > 1000): ?>
2630 <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;">
2631 <span class="dashicons dashicons-info"></span>
2632 <span>
2633 <?php printf(
2634 esc_html__('Showing your %s most recent entries for faster loading. Your complete database contains %s entries. ', 'mxchat'),
2635 number_format($total_records),
2636 number_format($total_in_database)
2637 ); ?>
2638 <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('View complete database in Pinecone →', 'mxchat'); ?></a>
2639 </span>
2640 </div>
2641 <?php endif; ?>
2642
2643 <!-- Data Source Info Banner -->
2644 <?php if ($use_pinecone) : ?>
2645 <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;">
2646 <span class="dashicons dashicons-cloud"></span>
2647 <span><?php esc_html_e('Refresh page to see new content. New entires or deletions can take up to 60 seconds to reflect! Refreshing too early means content won\'t show and you\'ll need to refresh again.', 'mxchat'); ?></span>
2648 </div>
2649 <?php else : ?>
2650 <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;">
2651 <span class="dashicons dashicons-database-view"></span>
2652 <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>
2653 </div>
2654 <?php endif; ?>
2655
2656 <!-- Table -->
2657 <div class="mxchat-table-wrapper">
2658 <table class="mxchat-records-table">
2659 <thead>
2660 <tr>
2661 <th><?php esc_html_e('ID', 'mxchat'); ?></th>
2662 <th><?php esc_html_e('Content', 'mxchat'); ?></th>
2663 <th><?php esc_html_e('Source', 'mxchat'); ?></th>
2664 <?php if ($data_source === 'pinecone') : ?>
2665 <th><?php esc_html_e('Vector ID', 'mxchat'); ?></th>
2666 <?php endif; ?>
2667 <th><?php esc_html_e('Actions', 'mxchat'); ?></th>
2668 </tr>
2669 </thead>
2670 <tbody>
2671 <?php if ($prompts) : ?>
2672 <?php foreach ($prompts as $index => $prompt) : ?>
2673 <tr id="prompt-<?php echo esc_attr($prompt->id); ?>"
2674 data-source="<?php echo esc_attr($data_source); ?>"
2675 <?php if ($data_source === 'pinecone') : ?>
2676 style="background: rgba(33, 150, 243, 0.02);"
2677 <?php endif; ?>>
2678 <td>
2679 <?php if ($data_source === 'pinecone') : ?>
2680 <?php echo esc_html($index + 1 + (($current_page - 1) * $per_page)); ?>
2681 <?php else : ?>
2682 <?php echo esc_html($prompt->id); ?>
2683 <?php endif; ?>
2684 </td>
2685 <td class="mxchat-content-cell">
2686 <div class="mxchat-accordion-wrapper">
2687 <div class="mxchat-content-preview">
2688 <?php
2689 $content = $prompt->article_content;
2690 $preview_length = 150;
2691 $content_preview = mb_strlen($content) > $preview_length
2692 ? mb_substr($content, 0, $preview_length) . '...'
2693 : $content;
2694 ?>
2695 <span class="preview-text"><?php echo esc_html($content_preview); ?></span>
2696 <?php if (mb_strlen($content) > $preview_length) : ?>
2697 <button class="mxchat-expand-toggle" type="button">
2698 <span class="dashicons dashicons-arrow-down-alt2"></span>
2699 </button>
2700 <?php endif; ?>
2701 </div>
2702 <div class="mxchat-content-full" style="display: none;">
2703 <div class="content-view">
2704 <?php
2705 // Check if content contains Hebrew characters
2706 if (preg_match('/[\x{0590}-\x{05FF}]/u', $content)) {
2707 // Apply RTL direction for Hebrew content
2708 echo '<div dir="rtl" lang="he" class="rtl-content">';
2709 echo wp_kses_post(wpautop($content));
2710 echo '</div>';
2711 } else {
2712 echo wp_kses_post(wpautop($content));
2713 }
2714 ?>
2715 </div>
2716 <?php if ($data_source === 'wordpress') : ?>
2717 <textarea class="content-edit" style="display:none;"
2718 <?php if (preg_match('/[\x{0590}-\x{05FF}]/u', $prompt->article_content)) echo 'dir="rtl" lang="he"'; ?>>
2719 <?php echo htmlspecialchars($prompt->article_content, ENT_QUOTES, 'UTF-8'); ?>
2720 </textarea>
2721 <?php endif; ?>
2722 </div>
2723 </div>
2724 </td>
2725 <td class="mxchat-url-cell">
2726 <div class="url-view">
2727 <?php if (!empty($prompt->source_url)) : ?>
2728 <a href="<?php echo esc_url($prompt->source_url); ?>" target="_blank">
2729 <span class="dashicons dashicons-external"></span>
2730 <?php esc_html_e('View Source', 'mxchat'); ?>
2731 </a>
2732 <?php else : ?>
2733 <span class="mxchat-na"><?php esc_html_e('N/A', 'mxchat'); ?></span>
2734 <?php endif; ?>
2735 </div>
2736 <?php if ($data_source === 'wordpress') : ?>
2737 <input type="text" class="url-edit" style="display:none;"
2738 value="<?php echo htmlspecialchars($prompt->source_url, ENT_QUOTES, 'UTF-8'); ?>" />
2739 <?php endif; ?>
2740 </td>
2741 <?php if ($data_source === 'pinecone') : ?>
2742 <td class="mxchat-vector-id-cell">
2743 <code style="background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 11px;">
2744 <?php echo esc_html(substr($prompt->id, 0, 12) . '...'); ?>
2745 </code>
2746 </td>
2747 <?php endif; ?>
2748
2749 <td class="mxchat-actions-cell">
2750 <?php if ($data_source === 'wordpress') : ?>
2751 <!-- WordPress - Full editing capabilities -->
2752 <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2753 <select class="mxchat-role-select"
2754 data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2755 data-data-source="wordpress"
2756 data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2757 <?php
2758 $current_role = $prompt->role_restriction ?? 'public';
2759 $role_options = $knowledge_manager->mxchat_get_role_options();
2760 foreach ($role_options as $role_key => $role_label) : ?>
2761 <option value="<?php echo esc_attr($role_key); ?>"
2762 <?php selected($current_role, $role_key); ?>>
2763 <?php echo esc_html($role_label); ?>
2764 </option>
2765 <?php endforeach; ?>
2766 </select>
2767 </div>
2768
2769 <!-- Edit/Save Buttons -->
2770 <div class="mxchat-action-buttons">
2771 <button class="mxchat-button-icon edit-button"
2772 data-id="<?php echo esc_attr($prompt->id); ?>">
2773 <span class="dashicons dashicons-edit"></span>
2774 </button>
2775 <button class="mxchat-button-icon save-button"
2776 data-id="<?php echo esc_attr($prompt->id); ?>"
2777 style="display:none;"
2778 data-nonce="<?php echo wp_create_nonce('mxchat_save_inline_nonce'); ?>">
2779 <span class="dashicons dashicons-saved"></span>
2780 </button>
2781 </div>
2782 <?php else : ?>
2783 <!-- Pinecone - Role restriction editable, content read-only -->
2784 <div class="mxchat-role-restriction-container" style="margin-bottom: 8px;">
2785 <select class="mxchat-role-select"
2786 data-entry-id="<?php echo esc_attr($prompt->id); ?>"
2787 data-data-source="pinecone"
2788 data-nonce="<?php echo wp_create_nonce('mxchat_update_role_nonce'); ?>">
2789 <?php
2790 $current_role = $prompt->role_restriction ?? 'public';
2791 $role_options = $knowledge_manager->mxchat_get_role_options();
2792 foreach ($role_options as $role_key => $role_label) : ?>
2793 <option value="<?php echo esc_attr($role_key); ?>"
2794 <?php selected($current_role, $role_key); ?>>
2795 <?php echo esc_html($role_label); ?>
2796 </option>
2797 <?php endforeach; ?>
2798 </select>
2799 </div>
2800
2801 <?php endif; ?>
2802
2803 <!-- Delete button for both sources -->
2804 <div class="mxchat-delete-container">
2805 <?php if ($data_source === 'pinecone') : ?>
2806 <!-- AJAX Delete for Pinecone -->
2807 <button type="button"
2808 class="mxchat-button-icon delete-button-ajax"
2809 data-vector-id="<?php echo esc_attr($prompt->id); ?>"
2810 data-bot-id="<?php echo esc_attr($current_bot_id); ?>"
2811 data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
2812 title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
2813 <span class="dashicons dashicons-trash"></span>
2814 </button>
2815 <?php else : ?>
2816 <!-- Regular link delete for WordPress DB -->
2817 <a href="<?php echo esc_url(admin_url(
2818 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
2819 . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
2820 )); ?>"
2821 class="mxchat-button-icon delete-button"
2822 onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
2823 <span class="dashicons dashicons-trash"></span>
2824 </a>
2825 <?php endif; ?>
2826 </div>
2827 </td>
2828
2829 </tr>
2830 <?php endforeach; ?>
2831 <?php else : ?>
2832 <tr>
2833 <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
2834 <?php if ($use_pinecone) : ?>
2835 <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
2836 <?php else : ?>
2837 <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
2838 <?php endif; ?>
2839 </td>
2840 </tr>
2841 <?php endif; ?>
2842 </tbody>
2843 </table>
2844 </div>
2845
2846 <?php if ($page_links) : ?>
2847 <div class="mxchat-pagination">
2848 <?php echo wp_kses_post($page_links); ?>
2849 </div>
2850 <?php endif; ?>
2851 </div>
2852
2853 </div>
2854
2855 <!-- Settings Tab (Renamed from Auto-Sync Settings) -->
2856 <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
2857
2858 <!-- Auto-Sync Settings Card -->
2859 <div class="mxchat-card">
2860 <div class="mxchat-settings-section">
2861 <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
2862 <p class="mxchat-description">
2863 <?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'); ?>
2864 </p>
2865 <div class="mxchat-autosave-section">
2866 <div class="mxchat-toggle-group">
2867 <div class="mxchat-toggle-container">
2868 <label class="mxchat-toggle-switch">
2869 <input type="checkbox"
2870 name="mxchat_auto_sync_posts"
2871 class="mxchat-autosave-field"
2872 value="1"
2873 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2874 <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
2875 <span class="mxchat-toggle-slider"></span>
2876 </label>
2877 <span class="mxchat-toggle-label">
2878 <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
2879 </span>
2880 </div>
2881
2882 <div class="mxchat-toggle-container">
2883 <label class="mxchat-toggle-switch">
2884 <input type="checkbox"
2885 name="mxchat_auto_sync_pages"
2886 class="mxchat-autosave-field"
2887 value="1"
2888 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2889 <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
2890 <span class="mxchat-toggle-slider"></span>
2891 </label>
2892 <span class="mxchat-toggle-label">
2893 <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
2894 </span>
2895 </div>
2896
2897 <!-- Custom Post Types Section -->
2898 <div class="mxchat-section-content">
2899 <div class="mxchat-custom-post-types-header">
2900 <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
2901 <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
2902 <span class="mxchat-toggle-icon">▼</span>
2903 </button>
2904 </div>
2905
2906 <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
2907 <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
2908 <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
2909
2910 <div class="mxchat-custom-post-types">
2911 <?php
2912 $post_types = $knowledge_manager->mxchat_get_public_post_types();
2913 // Skip post and page as they're handled separately
2914 unset($post_types['post']);
2915 unset($post_types['page']);
2916
2917 if (!empty($post_types)) {
2918 foreach ($post_types as $post_type => $label) {
2919 $option_name = 'mxchat_auto_sync_' . $post_type;
2920 $is_enabled = get_option($option_name, '0');
2921 ?>
2922 <div class="mxchat-toggle-container">
2923 <label class="mxchat-toggle-switch">
2924 <input type="checkbox"
2925 name="<?php echo esc_attr($option_name); ?>"
2926 class="mxchat-autosave-field"
2927 value="1"
2928 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
2929 <?php checked($is_enabled, '1'); ?>>
2930 <span class="mxchat-toggle-slider"></span>
2931 </label>
2932 <span class="mxchat-toggle-label">
2933 <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
2934 </span>
2935 </div>
2936 <?php
2937 }
2938 } else {
2939 echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
2940 }
2941 ?>
2942 </div>
2943 </div>
2944 </div>
2945 </div>
2946 </div>
2947 </div>
2948 </div>
2949
2950 <!-- Role-Based Content Restrictions Card -->
2951 <div class="mxchat-card" style="margin-top: 20px;">
2952 <h3><?php esc_html_e('Role-Based Content Restrictions', 'mxchat'); ?></h3>
2953 <p class="mxchat-description">
2954 <?php esc_html_e('Automatically restrict content access based on WordPress tags. When you assign a tag to a role, any post with that tag will only be accessible to users with that role or higher in the chatbot knowledge base.', 'mxchat'); ?>
2955 </p>
2956
2957 <!-- Info Box -->
2958 <div class="mxchat-knowledge-warning info" style="background: #e3f2fd; border-left-color: #2196f3;">
2959 <p>
2960 <span class="dashicons dashicons-info"></span>
2961 <strong><?php esc_html_e('How it works:', 'mxchat'); ?></strong>
2962 <?php esc_html_e('Add a tag below and select which role should have access. Any content tagged with that tag will automatically be restricted to that role level. Works with both auto-sync and manual imports.', 'mxchat'); ?>
2963 </p>
2964 </div>
2965
2966 <!-- Add New Tag-Role Mapping -->
2967 <div class="mxchat-role-mapping-form">
2968 <h4><?php esc_html_e('Add Tag-Role Mapping', 'mxchat'); ?></h4>
2969
2970 <div class="mxchat-form-row" style="display: flex; gap: 15px; align-items: flex-end; margin-bottom: 20px;">
2971 <div class="mxchat-form-group" style="flex: 1;">
2972 <label for="mxchat-tag-input">
2973 <?php esc_html_e('Tag Name', 'mxchat'); ?>
2974 </label>
2975 <input type="text"
2976 id="mxchat-tag-input"
2977 class="regular-text"
2978 placeholder="<?php esc_attr_e('Enter tag name (e.g., premium, members-only)', 'mxchat'); ?>" />
2979 <p class="description">
2980 <?php esc_html_e('Enter the exact tag slug as it appears in WordPress', 'mxchat'); ?>
2981 </p>
2982 </div>
2983
2984 <div class="mxchat-form-group" style="flex: 1;">
2985 <label for="mxchat-role-select">
2986 <?php esc_html_e('Required Role', 'mxchat'); ?>
2987 </label>
2988 <select id="mxchat-role-select" class="regular-text">
2989 <?php
2990 // Get role options from knowledge manager
2991 $role_options = array(
2992 'public' => __('Public (Everyone)', 'mxchat'),
2993 'logged_in' => __('Logged In Users', 'mxchat'),
2994 'subscriber' => __('Subscribers & Above', 'mxchat'),
2995 'contributor' => __('Contributors & Above', 'mxchat'),
2996 'author' => __('Authors & Above', 'mxchat'),
2997 'editor' => __('Editors & Above', 'mxchat'),
2998 'administrator' => __('Administrators Only', 'mxchat')
2999 );
3000
3001 foreach ($role_options as $role_key => $role_label) : ?>
3002 <option value="<?php echo esc_attr($role_key); ?>">
3003 <?php echo esc_html($role_label); ?>
3004 </option>
3005 <?php endforeach; ?>
3006 </select>
3007 <p class="description">
3008 <?php esc_html_e('Select the minimum role required to access this content', 'mxchat'); ?>
3009 </p>
3010 </div>
3011
3012 <div style="padding-bottom: 25px;">
3013 <button type="button"
3014 id="mxchat-add-tag-role"
3015 class="mxchat-button-primary">
3016 <span class="dashicons dashicons-plus-alt"></span>
3017 <?php esc_html_e('Add Mapping', 'mxchat'); ?>
3018 </button>
3019 </div>
3020 </div>
3021 </div>
3022
3023 <!-- Existing Tag-Role Mappings -->
3024 <div class="mxchat-role-mappings-list">
3025 <h4><?php esc_html_e('Current Tag-Role Mappings', 'mxchat'); ?></h4>
3026
3027 <div id="mxchat-mappings-container">
3028 <!-- Mappings will be loaded here via AJAX -->
3029 <div class="mxchat-loading-mappings">
3030 <span class="mxchat-spinner is-active"></span>
3031 <?php esc_html_e('Loading mappings...', 'mxchat'); ?>
3032 </div>
3033 </div>
3034
3035 <!-- Empty State (initially hidden) -->
3036 <div id="mxchat-no-mappings" style="display: none; text-align: center; padding: 40px; color: #666;">
3037 <span class="dashicons dashicons-tag" style="font-size: 48px; opacity: 0.3;"></span>
3038 <p><?php esc_html_e('No tag-role mappings yet. Add your first mapping above to get started.', 'mxchat'); ?></p>
3039 </div>
3040 </div>
3041
3042 <!-- Bulk Update Section -->
3043 <div class="mxchat-bulk-update-section" style="margin-top: 30px; padding-top: 30px; border-top: 1px solid #ddd;">
3044 <h4><?php esc_html_e('Bulk Update Existing Content', 'mxchat'); ?></h4>
3045 <p class="mxchat-description">
3046 <?php esc_html_e('Apply role restrictions to all existing content that has the mapped tags. This is useful when setting up tag-role mappings for the first time or after changing mappings.', 'mxchat'); ?>
3047 </p>
3048
3049 <div class="mxchat-bulk-update-controls">
3050 <button type="button"
3051 id="mxchat-bulk-update-roles"
3052 class="mxchat-button-secondary">
3053 <span class="dashicons dashicons-update"></span>
3054 <?php esc_html_e('Update All Existing Content', 'mxchat'); ?>
3055 </button>
3056
3057 <div id="mxchat-bulk-update-progress" style="display: none; margin-top: 15px;">
3058 <div class="mxchat-progress-bar">
3059 <div class="mxchat-progress-fill" style="width: 0%"></div>
3060 </div>
3061 <p class="mxchat-progress-text" style="margin-top: 10px; color: #666;">
3062 <?php esc_html_e('Processing...', 'mxchat'); ?>
3063 </p>
3064 </div>
3065
3066 <div id="mxchat-bulk-update-result" style="display: none; margin-top: 15px;"></div>
3067 </div>
3068 </div>
3069 </div>
3070
3071 </div>
3072
3073 <!-- Pinecone Settings Tab -->
3074 <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
3075 <div class="mxchat-card">
3076 <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
3077 <p class="mxchat-description">
3078 <?php echo wp_kses(
3079 __('<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'),
3080 array('strong' => array())
3081 ); ?>
3082 </p>
3083
3084 <div class="mxchat-pinecone-info-box">
3085 <div class="mxchat-info-icon">
3086 <span class="dashicons dashicons-info"></span>
3087 </div>
3088 <div class="mxchat-info-content">
3089 <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
3090 <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>
3091 <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
3092 </div>
3093 </div>
3094
3095 <div class="mxchat-database-settings-form">
3096 <?php
3097 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
3098 $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
3099 ?>
3100
3101 <div class="mxchat-toggle-container">
3102 <label class="mxchat-toggle-switch">
3103 <input type="checkbox"
3104 name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
3105 value="1"
3106 <?php checked($use_pinecone, '1'); ?>>
3107 <span class="mxchat-toggle-slider"></span>
3108 </label>
3109 <span class="mxchat-toggle-label">
3110 <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
3111 </span>
3112 </div>
3113
3114 <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
3115
3116 <?php if ($use_pinecone) : ?>
3117 <div class="mxchat-knowledge-warning success">
3118 <p><span class="dashicons dashicons-yes-alt"></span>
3119 <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
3120 </p>
3121 </div>
3122 <?php endif; ?>
3123
3124 <div class="mxchat-form-group">
3125 <label for="mxchat_pinecone_api_key">
3126 <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
3127 </label>
3128 <input type="password"
3129 id="mxchat_pinecone_api_key"
3130 name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
3131 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
3132 class="regular-text"
3133 placeholder="pcsk_..." />
3134 <p class="description">
3135 <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
3136 <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
3137 </p>
3138 </div>
3139
3140 <div class="mxchat-form-group">
3141 <label for="mxchat_pinecone_environment">
3142 <?php esc_html_e('Region', 'mxchat'); ?>
3143 </label>
3144 <input type="text"
3145 id="mxchat_pinecone_environment"
3146 name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
3147 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
3148 placeholder="e.g., gcp-starter"
3149 class="regular-text" />
3150 <p class="description">
3151 <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
3152 </p>
3153 </div>
3154
3155 <div class="mxchat-form-group">
3156 <label for="mxchat_pinecone_index">
3157 <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
3158 </label>
3159 <input type="text"
3160 id="mxchat_pinecone_index"
3161 name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
3162 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
3163 placeholder="e.g., my-wordpress-vectors"
3164 class="regular-text" />
3165 <p class="description">
3166 <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
3167 </p>
3168 </div>
3169
3170 <div class="mxchat-form-group">
3171 <label for="mxchat_pinecone_host">
3172 <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
3173 </label>
3174 <input type="text"
3175 id="mxchat_pinecone_host"
3176 name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
3177 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
3178 placeholder="e.g., my-index-xyz123.svc.pinecone.io"
3179 class="regular-text" />
3180 <p class="description">
3181 <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
3182 </p>
3183 </div>
3184
3185 <div class="mxchat-setup-steps">
3186 <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
3187 <div class="mxchat-setup-step">
3188 <span class="step-number">1</span>
3189 <div class="step-content">
3190 <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
3191 <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
3192 </div>
3193 </div>
3194 <div class="mxchat-setup-step">
3195 <span class="step-number">2</span>
3196 <div class="step-content">
3197 <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
3198 <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
3199 <ul>
3200 <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>
3201 <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
3202 <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
3203 </ul>
3204 </div>
3205 </div>
3206 <div class="mxchat-setup-step">
3207 <span class="step-number">3</span>
3208 <div class="step-content">
3209 <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
3210 <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
3211 </div>
3212 </div>
3213 <div class="mxchat-setup-step">
3214 <span class="step-number">4</span>
3215 <div class="step-content">
3216 <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
3217 <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
3218 </div>
3219 </div>
3220 <div class="mxchat-setup-step">
3221 <span class="step-number">5</span>
3222 <div class="step-content">
3223 <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
3224 <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
3225 </div>
3226 </div>
3227 </div>
3228
3229 </div>
3230
3231 </div>
3232 </div>
3233 </div>
3234
3235 </div>
3236
3237 </div>
3238 </div>
3239
3240 <!-- Content Selector Modal -->
3241 <div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
3242 <div class="mxchat-kb-modal-content">
3243 <div class="mxchat-kb-modal-header">
3244 <h3>
3245 <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
3246 <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
3247 </h3>
3248 <span class="mxchat-kb-modal-close">&times;</span>
3249 </div>
3250 <div class="mxchat-kb-modal-filters">
3251 <div class="mxchat-kb-search-group">
3252 <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
3253 </div>
3254
3255 <div class="mxchat-kb-filter-group">
3256 <select id="mxchat-kb-content-type-filter">
3257 <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
3258 <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
3259 <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
3260 <?php
3261 // Add other post types dynamically
3262 $post_types = get_post_types(array('public' => true), 'objects');
3263 foreach ($post_types as $post_type) {
3264 // Skip post and page as they're already added
3265 if (!in_array($post_type->name, array('post', 'page'))) {
3266 echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
3267 }
3268 }
3269 ?>
3270 </select>
3271
3272 <select id="mxchat-kb-content-status-filter">
3273 <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
3274 <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
3275 <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
3276 </select>
3277
3278 <select id="mxchat-kb-processed-filter">
3279 <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
3280 <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
3281 <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
3282 </select>
3283 </div>
3284 </div>
3285
3286 <div class="mxchat-kb-content-selection">
3287 <div class="mxchat-kb-selection-header">
3288 <label>
3289 <input type="checkbox" id="mxchat-kb-select-all">
3290 <?php esc_html_e('Select All', 'mxchat'); ?>
3291 </label>
3292 <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
3293 </div>
3294
3295 <div class="mxchat-kb-content-list">
3296 <!-- Content will be loaded here via AJAX -->
3297 <div class="mxchat-kb-loading">
3298 <span class="mxchat-kb-spinner is-active"></span>
3299 <?php esc_html_e('Loading content...', 'mxchat'); ?>
3300 </div>
3301 </div>
3302
3303 <div class="mxchat-kb-pagination">
3304 <!-- Pagination will be added here -->
3305 </div>
3306 </div>
3307
3308 <div class="mxchat-kb-modal-footer">
3309 <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
3310 <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
3311 <span class="mxchat-kb-selected-count">(0)</span>
3312 </button>
3313 <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
3314 <?php esc_html_e('Cancel', 'mxchat'); ?>
3315 </button>
3316 </div>
3317 </div>
3318 </div>
3319
3320 <?php
3321 }
3322
3323
3324
3325
3326
3327
3328 /**
3329 * Get bot-specific Pinecone configuration
3330 * Used in the knowledge retrieval functions
3331 */
3332 private function get_bot_pinecone_config($bot_id = 'default') {
3333 //error_log("MXCHAT DEBUG: get_bot_pinecone_config called for bot: " . $bot_id);
3334
3335 // If default bot or multi-bot add-on not active, use default Pinecone config
3336 if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
3337 //error_log("MXCHAT DEBUG: Using default Pinecone config (no multi-bot or bot is 'default')");
3338 $addon_options = get_option('mxchat_pinecone_addon_options', array());
3339 $config = array(
3340 'use_pinecone' => (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1'),
3341 'api_key' => $addon_options['mxchat_pinecone_api_key'] ?? '',
3342 'host' => $addon_options['mxchat_pinecone_host'] ?? '',
3343 'namespace' => $addon_options['mxchat_pinecone_namespace'] ?? ''
3344 );
3345 //error_log("MXCHAT DEBUG: Default config - use_pinecone: " . ($config['use_pinecone'] ? 'true' : 'false'));
3346 return $config;
3347 }
3348
3349 //error_log("MXCHAT DEBUG: Calling filter 'mxchat_get_bot_pinecone_config' for bot: " . $bot_id);
3350
3351 // Hook for multi-bot add-on to provide bot-specific Pinecone config
3352 $bot_pinecone_config = apply_filters('mxchat_get_bot_pinecone_config', array(), $bot_id);
3353
3354 if (!empty($bot_pinecone_config)) {
3355 //error_log("MXCHAT DEBUG: Got bot-specific config from filter");
3356 //error_log(" - use_pinecone: " . (isset($bot_pinecone_config['use_pinecone']) ? ($bot_pinecone_config['use_pinecone'] ? 'true' : 'false') : 'not set'));
3357 //error_log(" - host: " . ($bot_pinecone_config['host'] ?? 'not set'));
3358 //error_log(" - namespace: " . ($bot_pinecone_config['namespace'] ?? 'not set'));
3359 } else {
3360 //error_log("MXCHAT DEBUG: Filter returned empty config!");
3361 }
3362
3363 return is_array($bot_pinecone_config) ? $bot_pinecone_config : array();
3364 }
3365
3366 public function mxchat_delete_chat_history() {
3367 if (!current_user_can('manage_options')) {
3368 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
3369 wp_die();
3370 }
3371 check_ajax_referer('mxchat_delete_chat_history', 'security');
3372 global $wpdb;
3373 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
3374
3375 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
3376 $deleted_count = 0;
3377
3378 foreach ($_POST['delete_session_ids'] as $session_id) {
3379 $session_id_sanitized = sanitize_text_field($session_id);
3380
3381 // Clear relevant cache before deletion
3382 $cache_key = 'chat_session_' . $session_id_sanitized;
3383 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
3384
3385 // Perform the deletion from the database table
3386 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
3387
3388 // Delete the corresponding option entry from wp_options table
3389 delete_option("mxchat_history_" . $session_id_sanitized);
3390
3391 // Delete any associated metadata options
3392 delete_option("mxchat_email_" . $session_id_sanitized);
3393 delete_option("mxchat_agent_name_" . $session_id_sanitized);
3394
3395 $deleted_count++;
3396 }
3397
3398 // Optionally, clear a general cache if you have one
3399 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
3400
3401 echo wp_json_encode([
3402 'success' => sprintf(
3403 esc_html__('%d chat session(s) have been deleted from all storage locations.', 'mxchat'),
3404 $deleted_count
3405 )
3406 ]);
3407 } else {
3408 echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
3409 }
3410
3411 wp_die();
3412 }
3413 public function display_admin_notices() {
3414 // Check if we're on a MXChat admin page
3415 $screen = get_current_screen();
3416 if (!$screen || strpos($screen->base, 'mxchat') === false) {
3417 return;
3418 }
3419
3420 //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
3421
3422 // Check for error notices
3423 $error_notice = get_transient('mxchat_admin_notice_error');
3424 if ($error_notice) {
3425 //error_log('Found error transient: ' . $error_notice);
3426 echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
3427 delete_transient('mxchat_admin_notice_error');
3428 //error_log('Displayed and deleted error transient');
3429 } else {
3430 //error_log('No error transient found');
3431 }
3432
3433 // Check for success notices
3434 $success_notice = get_transient('mxchat_admin_notice_success');
3435 if ($success_notice) {
3436 //error_log('Found success transient: ' . $success_notice);
3437 echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
3438 delete_transient('mxchat_admin_notice_success');
3439 //error_log('Displayed and deleted success transient');
3440 }
3441
3442 // Check for info notices
3443 $info_notice = get_transient('mxchat_admin_notice_info');
3444 if ($info_notice) {
3445 //error_log('Found info transient: ' . $info_notice);
3446 echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
3447 delete_transient('mxchat_admin_notice_info');
3448 //error_log('Displayed and deleted info transient');
3449 }
3450
3451 }
3452
3453
3454 public function mxchat_create_activation_page() {
3455 $license_status = get_option('mxchat_license_status', 'inactive');
3456 $license_error = get_option('mxchat_license_error', '');
3457 $current_domain = parse_url(home_url(), PHP_URL_HOST);
3458
3459 $license_management_url = 'https://mxchat.ai/my-account/orders/';
3460
3461 // Check if this activation has been linked to a domain
3462 $is_domain_linked = $this->is_current_activation_linked($current_domain);
3463 ?>
3464 <div class="wrap mxchat-admin-activation">
3465 <div class="mxchat-pro-hero">
3466 <h1 class="pro-title">
3467 <span class="pro-gradient-text">Manage</span> MxChat Pro
3468 </h1>
3469 <p class="pro-subtitle">
3470 <?php if ($license_status === 'active'): ?>
3471 <?php esc_html_e('Your pro license is active. Manage your activation below.', 'mxchat'); ?>
3472 <?php else: ?>
3473 <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
3474 <?php endif; ?>
3475 </p>
3476 </div>
3477
3478 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
3479 <div class="error notice">
3480 <p><?php echo esc_html($license_error); ?></p>
3481 </div>
3482 <?php endif; ?>
3483
3484 <!-- Domain Linking Notice for Existing Users -->
3485 <?php if ($license_status === 'active' && !$is_domain_linked): ?>
3486 <div class="notice notice-info">
3487 <p>
3488 <strong><?php esc_html_e('New Feature: Domain Tracking', 'mxchat'); ?></strong><br>
3489 <?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'); ?>
3490 </p>
3491 <p>
3492 <button type="button" id="link-domain-button" class="button button-secondary">
3493 <?php esc_html_e('Link This Domain', 'mxchat'); ?>
3494 </button>
3495 <span id="domain-link-status" style="margin-left: 10px;"></span>
3496 </p>
3497 </div>
3498 <?php endif; ?>
3499
3500 <!-- Activation Form -->
3501 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
3502 <div class="mxchat-pro-form-container">
3503 <table class="form-table">
3504 <tr valign="top">
3505 <th scope="row"><?php esc_html_e('Email Address', 'mxchat'); ?></th>
3506 <td>
3507 <input type="email" id="mxchat_pro_email" name="mxchat_pro_email" value="<?php echo esc_attr(get_option('mxchat_pro_email')); ?>" class="regular-text mxchat-pro-input" required />
3508 </td>
3509 </tr>
3510 <tr valign="top">
3511 <th scope="row"><?php esc_html_e('Activation Key', 'mxchat'); ?></th>
3512 <td>
3513 <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 />
3514 </td>
3515 </tr>
3516 </table>
3517
3518 <!-- Hidden field for domain -->
3519 <input type="hidden" id="mxchat_domain" name="domain" value="<?php echo esc_attr($current_domain); ?>" />
3520
3521 <?php if ($license_status !== 'active'): ?>
3522 <div class="mxchat-pro-button-container">
3523 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
3524 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
3525 </div>
3526 <?php endif; ?>
3527 </div>
3528 </form>
3529
3530 <!-- License Status Display -->
3531 <div class="mxchat-pro-status">
3532 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
3533 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
3534 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
3535 </span>
3536 </h3>
3537
3538 <?php if ($license_status === 'active'): ?>
3539 <div class="mxchat-license-details">
3540 <p><strong><?php esc_html_e('Domain:', 'mxchat'); ?></strong> <?php echo esc_html($current_domain); ?>
3541 <?php if ($is_domain_linked): ?>
3542 <span style="color: green; margin-left: 10px;">✓ <?php esc_html_e('Linked', 'mxchat'); ?></span>
3543 <?php else: ?>
3544 <span style="color: orange; margin-left: 10px;">⚠ <?php esc_html_e('Not Linked', 'mxchat'); ?></span>
3545 <?php endif; ?>
3546 </p>
3547 <p><strong><?php esc_html_e('Email:', 'mxchat'); ?></strong> <?php echo esc_html(get_option('mxchat_pro_email')); ?></p>
3548 <p><strong><?php esc_html_e('License Key:', 'mxchat'); ?></strong>
3549 <code style="background: #f1f1f1; padding: 2px 6px; border-radius: 3px;">
3550 <?php echo esc_html(get_option('mxchat_activation_key')); ?>
3551 </code>
3552 </p>
3553 <p>
3554 <small>
3555 <?php esc_html_e('Manage all your licenses and domains on', 'mxchat'); ?>
3556 <a href="<?php echo esc_url($license_management_url); ?>" target="_blank">
3557 <?php esc_html_e('your account dashboard', 'mxchat'); ?>
3558 </a>
3559 </small>
3560 </p>
3561
3562 <!-- Deactivation Section -->
3563 <div class="mxchat-deactivation-section" style="margin-top: 20px; padding: 15px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px;">
3564 <h4 style="margin-top: 0; color: #856404;">
3565 <?php esc_html_e('License Management', 'mxchat'); ?>
3566 </h4>
3567 <p style="margin-bottom: 15px; color: #856404;">
3568 <?php esc_html_e('Need to move this license to another site? Deactivate it here first, then activate it on your new site.', 'mxchat'); ?>
3569 </p>
3570 <button type="button" id="deactivate-license-button" class="button button-secondary" style="background: #dc3545; color: white; border-color: #dc3545;">
3571 🔓 <?php esc_html_e('Deactivate License', 'mxchat'); ?>
3572 </button>
3573 <span id="deactivate-status" style="margin-left: 10px;"></span>
3574 </div>
3575 </div>
3576 <?php endif; ?>
3577 </div>
3578
3579 <!-- Hidden fields for JavaScript -->
3580 <input type="hidden" id="mxchat-ajax-url" value="<?php echo admin_url('admin-ajax.php'); ?>" />
3581 <input type="hidden" id="mxchat-nonce" value="<?php echo wp_create_nonce('mxchat_activate_license_nonce'); ?>" />
3582 <input type="hidden" id="mxchat-api-url" value="https://mxchat.ai" />
3583 </div>
3584 <?php
3585 }
3586
3587 /**
3588 * Check if current activation is linked to a domain
3589 * This checks YOUR website's database, not the user's local database
3590 */
3591 private function is_current_activation_linked($domain) {
3592 $license_key = get_option('mxchat_activation_key');
3593 $email = get_option('mxchat_pro_email');
3594
3595 if (empty($license_key) || empty($email)) {
3596 return false;
3597 }
3598
3599 // Check with YOUR website's API
3600 $response = wp_remote_post('https://mxchat.ai/mxchat-api/check-domain', array(
3601 'body' => array(
3602 'license_key' => $license_key,
3603 'email' => $email,
3604 'domain' => $domain
3605 ),
3606 'timeout' => 10,
3607 'sslverify' => false
3608 ));
3609
3610 if (is_wp_error($response)) {
3611 return false;
3612 }
3613
3614 $body = json_decode(wp_remote_retrieve_body($response), true);
3615 return isset($body['success']) && $body['success'] && isset($body['data']['linked']) && $body['data']['linked'];
3616 }
3617
3618
3619 public function mxchat_actions_page_html() {
3620 if (!current_user_can('manage_options')) {
3621 return;
3622 }
3623
3624 // Keep existing data fetching logic
3625 global $wpdb;
3626 $table_name = $wpdb->prefix . 'mxchat_intents';
3627 $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
3628 $per_page = 20;
3629 $offset = ($page - 1) * $per_page;
3630
3631 // Success message
3632 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
3633 echo '<div class="notice notice-success is-dismissible"><p>' .
3634 esc_html__('Action updated successfully.', 'mxchat') .
3635 '</p></div>';
3636 }
3637
3638 // Filtering logic
3639 $where = '1=1';
3640 $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
3641 $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
3642
3643 if ($search_term) {
3644 $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
3645 $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
3646 $search_term_like, $search_term_like);
3647 }
3648
3649 if ($callback_filter) {
3650 $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
3651 }
3652
3653 // Pagination
3654 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
3655 $total_pages = ceil($total_intents / $per_page);
3656
3657 // Get intents (now called actions)
3658 $actions = $wpdb->get_results($wpdb->prepare(
3659 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
3660 $per_page, $offset
3661 ));
3662
3663 // Get callbacks
3664 $available_callbacks = $this->mxchat_get_available_callbacks();
3665
3666 ?>
3667 <div class="wrap mxchat-wrapper">
3668 <!-- Hero Section -->
3669 <div class="mxchat-hero">
3670 <h1 class="mxchat-main-title">
3671 <span class="mxchat-gradient-text">Actions</span> Manager
3672 </h1>
3673 <p class="mxchat-hero-subtitle">
3674 <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
3675 </p>
3676 </div>
3677
3678 <!-- Actions Header with Search and Filter -->
3679 <div class="mxchat-actions-header">
3680 <div class="mxchat-actions-filters">
3681 <form method="get" class="mxchat-search-form">
3682 <input type="hidden" name="page" value="mxchat-actions">
3683 <div class="mxchat-search-group">
3684 <span class="dashicons dashicons-search"></span>
3685 <input type="text" name="s" class="mxchat-search-input"
3686 placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
3687 value="<?php echo esc_attr($search_term); ?>">
3688 </div>
3689 <select name="callback_filter" class="mxchat-action-filter">
3690 <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
3691 <?php foreach ($available_callbacks as $function => $callback_data) :
3692 $label = $callback_data['label']; ?>
3693 <option value="<?php echo esc_attr($function); ?>"
3694 <?php selected($callback_filter, $function); ?>>
3695 <?php echo esc_html($label); ?>
3696 </option>
3697 <?php endforeach; ?>
3698 </select>
3699 <button type="submit" class="mxchat-button-secondary">
3700 <?php esc_html_e('Filter', 'mxchat'); ?>
3701 </button>
3702 </form>
3703 </div>
3704 <div class="mxchat-actions-controls">
3705 <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
3706 <span class="dashicons dashicons-plus-alt"></span>
3707 <?php esc_html_e('Add New Action', 'mxchat'); ?>
3708 </button>
3709 </div>
3710 </div>
3711
3712 <!-- Actions Grid Layout - All actions in a single grid -->
3713 <div class="mxchat-actions-grid">
3714 <div class="mxchat-cards-container">
3715 <?php if (!empty($actions)) : ?>
3716 <?php foreach ($actions as $action) :
3717 $callback_function = $action->callback_function;
3718 $callback_label = isset($available_callbacks[$callback_function]['label'])
3719 ? $available_callbacks[$callback_function]['label']
3720 : $callback_function;
3721 $threshold_value = isset($action->similarity_threshold)
3722 ? round($action->similarity_threshold * 100)
3723 : 85;
3724
3725 // Check if this is a form action
3726 $is_form_action = strpos($action->intent_label, 'Form ') === 0;
3727
3728 // Get action status (enabled/disabled) - default to true if column doesn't exist
3729 $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
3730
3731 // Get enabled bots for display
3732 $enabled_bots = [];
3733 if (isset($action->enabled_bots) && !empty($action->enabled_bots)) {
3734 $enabled_bots = json_decode($action->enabled_bots, true);
3735 if (!is_array($enabled_bots)) {
3736 $enabled_bots = ['default'];
3737 }
3738 } else {
3739 $enabled_bots = ['default']; // Backward compatibility
3740 }
3741 ?>
3742 <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
3743 <div class="mxchat-card-header">
3744 <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
3745 <div class="mxchat-card-toggle">
3746 <label class="mxchat-switch">
3747 <input type="checkbox" class="mxchat-action-toggle"
3748 data-action-id="<?php echo esc_attr($action->id); ?>"
3749 <?php checked($is_enabled); ?>>
3750 <span class="mxchat-slider round"></span>
3751 </label>
3752 </div>
3753 </div>
3754
3755 <div class="mxchat-card-body">
3756 <div class="mxchat-card-description">
3757 <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
3758 <?php echo esc_html($callback_label); ?>
3759 </div>
3760
3761 <div class="mxchat-card-phrases">
3762 <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
3763 <div class="mxchat-phrases-preview">
3764 <?php
3765 // Check if the helper function exists, otherwise use a simple substring
3766 if (method_exists($this, 'get_trimmed_phrases')) {
3767 echo esc_html($this->get_trimmed_phrases($action->phrases));
3768 } else {
3769 echo esc_html(strlen($action->phrases) > 100 ?
3770 substr($action->phrases, 0, 97) . '...' :
3771 $action->phrases);
3772 }
3773 ?>
3774 </div>
3775 </div>
3776
3777 <div class="mxchat-threshold-control">
3778 <div class="mxchat-threshold-label">
3779 <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
3780 <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
3781 </div>
3782 </div>
3783
3784 <!-- Bot Availability Display (Read-only) -->
3785 <div class="mxchat-card-bots">
3786 <strong><?php esc_html_e('Assigned bots:', 'mxchat'); ?></strong>
3787 <div class="mxchat-bot-badges">
3788 <?php
3789 foreach ($enabled_bots as $bot_id) {
3790 if ($bot_id === 'default') {
3791 echo '<span class="mxchat-bot-badge">' . esc_html__('Default Bot', 'mxchat') . '</span>';
3792 } else {
3793 // Try to get bot name from multi-bot manager
3794 if (class_exists('MxChat_Multi_Bot_Core_Manager')) {
3795 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
3796 $available_bots = $multi_bot_manager->get_available_bots();
3797 $bot_name = isset($available_bots[$bot_id]) ? $available_bots[$bot_id] : $bot_id;
3798 echo '<span class="mxchat-bot-badge">' . esc_html($bot_name) . '</span>';
3799 } else {
3800 echo '<span class="mxchat-bot-badge">' . esc_html($bot_id) . '</span>';
3801 }
3802 }
3803 }
3804 ?>
3805 </div>
3806 </div>
3807 </div>
3808
3809 <div class="mxchat-card-footer">
3810 <?php
3811 // Check if it's a form action
3812 $is_form_action = preg_match('/Form (\d+)/', $action->intent_label, $form_matches);
3813
3814 // Check if it's a recommendation flow action
3815 $is_flow_action = preg_match('/Recommendation Flow (\d+)/', $action->intent_label, $flow_matches);
3816
3817 if ($is_form_action) {
3818 $form_id = isset($form_matches[1]) ? $form_matches[1] : '';
3819 ?>
3820 <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
3821 class="mxchat-button-primary">
3822 <span class="dashicons dashicons-feedback"></span>
3823 <?php esc_html_e('Edit Form', 'mxchat'); ?>
3824 </a>
3825 <?php } elseif ($is_flow_action) {
3826 ?>
3827 <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-smart-recommender')); ?>"
3828 class="mxchat-button-primary">
3829 <span class="dashicons dashicons-list-view"></span>
3830 <?php esc_html_e('Manage Flows', 'mxchat'); ?>
3831 </a>
3832 <?php } else { ?>
3833 <button type="button"
3834 class="mxchat-button-secondary mxchat-edit-button"
3835 data-action-id="<?php echo esc_attr($action->id); ?>"
3836 data-phrases="<?php echo esc_attr($action->phrases); ?>"
3837 data-label="<?php echo esc_attr($action->intent_label); ?>"
3838 data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
3839 data-callback-function="<?php echo esc_attr($action->callback_function); ?>"
3840 data-enabled-bots="<?php echo esc_attr(json_encode($enabled_bots)); ?>">
3841 <span class="dashicons dashicons-edit"></span>
3842 <?php esc_html_e('Edit', 'mxchat'); ?>
3843 </button>
3844 <form method="post"
3845 action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
3846 class="mxchat-delete-form"
3847 onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
3848 <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
3849 <input type="hidden" name="action" value="mxchat_delete_intent">
3850 <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
3851 <button type="submit" class="mxchat-button-text mxchat-delete-button">
3852 <span class="dashicons dashicons-trash"></span>
3853 <?php esc_html_e('Delete', 'mxchat'); ?>
3854 </button>
3855 </form>
3856 <?php } ?>
3857 </div>
3858 </div>
3859 <?php endforeach; ?>
3860 <?php else : ?>
3861 <!-- If no actions found -->
3862 <div class="mxchat-no-actions">
3863 <div class="mxchat-empty-state">
3864 <span class="dashicons dashicons-format-chat"></span>
3865 <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
3866 <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
3867 <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
3868 <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
3869 </button>
3870 </div>
3871 </div>
3872 <?php endif; ?>
3873 </div>
3874 </div>
3875
3876 <?php if ($total_pages > 1) : ?>
3877 <div class="mxchat-pagination">
3878 <?php
3879 echo paginate_links(array(
3880 'base' => add_query_arg('paged', '%#%'),
3881 'format' => '',
3882 'prev_text' => __('&laquo; Previous', 'mxchat'),
3883 'next_text' => __('Next &raquo;', 'mxchat'),
3884 'total' => $total_pages,
3885 'current' => $page
3886 ));
3887 ?>
3888 </div>
3889 <?php endif; ?>
3890
3891 <!-- Add/Edit Action Modal with Step-Based Approach -->
3892 <!-- Complete Modal HTML with Defined Groups Variable -->
3893 <div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
3894 <div class="mxchat-modal-content">
3895 <span class="mxchat-modal-close">&times;</span>
3896
3897 <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
3898 <!-- Dynamic nonce field -->
3899 <div id="action-nonce-container">
3900 <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
3901 </div>
3902 <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
3903 <input type="hidden" name="intent_id" id="edit_action_id" value="">
3904 <input type="hidden" name="callback_function" id="callback_function" value="">
3905
3906 <!-- Step 1: Action Type Selection -->
3907 <div id="mxchat-action-step-1" class="mxchat-action-step active">
3908 <div class="mxchat-step-indicator">
3909 <div class="mxchat-step-number">1</div>
3910 <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
3911 </div>
3912
3913 <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
3914 <div class="mxchat-action-type-search">
3915 <span class="dashicons dashicons-search"></span>
3916 <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
3917 </div>
3918
3919 <?php
3920 // Get the callbacks - IMPORTANT: Define the $groups variable here
3921 $groups = $this->mxchat_get_available_callbacks(true, true);
3922 ?>
3923
3924 <div class="mxchat-action-type-categories">
3925 <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
3926 <?php
3927 // Get unique categories from the defined groups
3928 foreach ($groups as $group_label => $group_callbacks) :
3929 $category_slug = sanitize_title($group_label);
3930 ?>
3931 <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
3932 <?php endforeach; ?>
3933 </div>
3934
3935 <div class="mxchat-action-types-grid">
3936 <?php
3937 // Generate action cards from available callbacks
3938 foreach ($groups as $group_label => $group_callbacks) :
3939 $category_slug = sanitize_title($group_label);
3940
3941 foreach ($group_callbacks as $function => $data) :
3942 $label = $data['label'];
3943 $pro_only = $data['pro_only'];
3944 $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
3945 $description = isset($data['description']) ? $data['description'] : '';
3946 $is_addon = isset($data['addon']) && $data['addon'] !== false;
3947 $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
3948 $is_installed = isset($data['installed']) ? $data['installed'] : true;
3949
3950 // Determine card status and styling
3951 $card_class = 'mxchat-action-type-card';
3952 $icon_class = 'mxchat-action-type-icon';
3953 $status_badge = '';
3954
3955 if ($pro_only && !$this->is_activated) {
3956 // Pro feature but no Pro license
3957 $icon_class .= ' pro-feature';
3958 $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
3959 }
3960
3961 if ($is_addon && !$is_installed) {
3962 // Add-on not installed
3963 $card_class .= ' not-installed';
3964 $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
3965 }
3966
3967 // Default description if none provided
3968 if (empty($description)) {
3969 $description = sprintf(
3970 esc_html__('Use the %s action in your chatbot', 'mxchat'),
3971 $label
3972 );
3973 }
3974 ?>
3975 <div class="<?php echo esc_attr($card_class); ?>"
3976 data-category="<?php echo esc_attr($category_slug); ?>"
3977 data-value="<?php echo esc_attr($function); ?>"
3978 data-label="<?php echo esc_attr($label); ?>"
3979 data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
3980 data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
3981 data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
3982 <div class="<?php echo esc_attr($icon_class); ?>">
3983 <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
3984 </div>
3985 <div class="mxchat-action-type-info">
3986 <h4><?php echo esc_html($label); ?></h4>
3987 <p><?php echo esc_html($description); ?></p>
3988 <?php if (!empty($status_badge)) : ?>
3989 <?php echo $status_badge; ?>
3990 <?php endif; ?>
3991
3992 <?php if ($is_addon && !$is_installed) : ?>
3993 <div class="mxchat-addon-info">
3994 <?php echo esc_html(sprintf(
3995 __('Requires %s', 'mxchat'),
3996 $addon_name
3997 )); ?>
3998 </div>
3999 <?php endif; ?>
4000 </div>
4001 </div>
4002 <?php endforeach;
4003 endforeach; ?>
4004 </div>
4005 </div>
4006
4007 <div class="mxchat-modal-actions">
4008 <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
4009 <?php esc_html_e('Cancel', 'mxchat'); ?>
4010 </button>
4011 </div>
4012 </div>
4013
4014 <!-- Step 2: Action Configuration -->
4015 <div id="mxchat-action-step-2" class="mxchat-action-step">
4016 <div class="mxchat-step-indicator">
4017 <div class="mxchat-step-number">2</div>
4018 <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
4019 </div>
4020
4021 <div class="mxchat-selected-action">
4022 <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
4023 <span class="dashicons dashicons-arrow-left-alt"></span>
4024 <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
4025 </button>
4026 <div class="mxchat-selected-action-info">
4027 <div id="selected-action-icon" class="mxchat-action-type-icon">
4028 <span class="dashicons dashicons-admin-generic"></span>
4029 </div>
4030 <div class="mxchat-selected-action-details">
4031 <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
4032 <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
4033 </div>
4034 </div>
4035 </div>
4036
4037 <div class="mxchat-form-group">
4038 <label for="intent_label">
4039 <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
4040 </label>
4041 <input name="intent_label" type="text" id="intent_label" required
4042 class="mxchat-intent-input"
4043 placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
4044 </div>
4045
4046 <div class="mxchat-form-group">
4047 <label for="phrases">
4048 <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
4049 </label>
4050 <textarea name="phrases" id="action_phrases" rows="5" required
4051 class="mxchat-intent-textarea"
4052 placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
4053 </div>
4054
4055 <div class="mxchat-form-group">
4056 <label for="similarity_threshold">
4057 <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
4058 <span class="mxchat-threshold-value-display">85%</span>
4059 </label>
4060 <div class="mxchat-slider-group modal-slider">
4061 <input type="range"
4062 name="similarity_threshold"
4063 id="similarity_threshold"
4064 min="10"
4065 max="95"
4066 value="85"
4067 class="mxchat-intent-slider"
4068 oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
4069 </div>
4070 <div class="mxchat-threshold-hint">
4071 <?php esc_html_e('Lower values (10-30) make the action trigger more easily. Higher values (70-95) require more exact matches.', 'mxchat'); ?>
4072 </div>
4073 </div>
4074
4075 <!-- Bot Selection Section -->
4076 <div class="mxchat-form-group">
4077 <label for="enabled_bots">
4078 <?php esc_html_e('Which bots should we enable this action for?', 'mxchat'); ?>
4079 </label>
4080 <div class="mxchat-bot-selector">
4081 <div class="mxchat-bot-option">
4082 <label class="mxchat-checkbox-label">
4083 <input type="checkbox"
4084 name="enabled_bots[]"
4085 value="default"
4086 id="bot_default"
4087 checked="checked">
4088 <span class="mxchat-checkmark"></span>
4089 <?php esc_html_e('Default Bot', 'mxchat'); ?>
4090 </label>
4091 </div>
4092
4093 <?php if (class_exists('MxChat_Multi_Bot_Core_Manager')) :
4094 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
4095 $available_bots = $multi_bot_manager->get_available_bots();
4096
4097 foreach ($available_bots as $bot_id => $bot_name) :
4098 if ($bot_id === 'default') continue; // Skip default, already shown above
4099 ?>
4100 <div class="mxchat-bot-option">
4101 <label class="mxchat-checkbox-label">
4102 <input type="checkbox"
4103 name="enabled_bots[]"
4104 value="<?php echo esc_attr($bot_id); ?>"
4105 id="bot_<?php echo esc_attr($bot_id); ?>">
4106 <span class="mxchat-checkmark"></span>
4107 <?php echo esc_html($bot_name); ?>
4108 </label>
4109 </div>
4110 <?php
4111 endforeach;
4112 endif; ?>
4113 </div>
4114 </div>
4115
4116 <div class="mxchat-modal-actions">
4117 <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
4118 <?php esc_html_e('Cancel', 'mxchat'); ?>
4119 </button>
4120 <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
4121 <?php esc_html_e('Save Action', 'mxchat'); ?>
4122 </button>
4123 </div>
4124 </div>
4125 </form>
4126 </div>
4127 </div>
4128
4129 <div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
4130 <div class="mxchat-action-loading-spinner"></div>
4131 <div class="mxchat-action-loading-text">
4132 <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
4133 </div>
4134 </div>
4135 </div><!-- .mxchat-wrapper -->
4136 <?php
4137 }
4138 private function get_trimmed_phrases($phrases, $max_length = 100) {
4139 if (strlen($phrases) <= $max_length) {
4140 return $phrases;
4141 }
4142
4143 $trimmed = substr($phrases, 0, $max_length);
4144 $last_comma = strrpos($trimmed, ',');
4145
4146 if ($last_comma !== false) {
4147 $trimmed = substr($trimmed, 0, $last_comma);
4148 }
4149
4150 return $trimmed . '...';
4151 }
4152 public function mxchat_add_enabled_column_to_intents() {
4153 global $wpdb;
4154 $table_name = $wpdb->prefix . 'mxchat_intents';
4155
4156 // Check if the column already exists
4157 $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
4158
4159 if (empty($columns)) {
4160 // Add the column with default value of 1 (enabled)
4161 $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
4162 }
4163 }
4164
4165 public function mxchat_handle_delete_intent() {
4166 if ( ! current_user_can( 'manage_options' ) ) {
4167 wp_die( esc_html__('Unauthorized user', 'mxchat') );
4168 }
4169
4170 check_admin_referer('mxchat_delete_intent_nonce');
4171
4172 if (isset($_POST['intent_id'])) {
4173 global $wpdb;
4174 $table_name = $wpdb->prefix . 'mxchat_intents';
4175 $intent_id = intval($_POST['intent_id']);
4176
4177 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
4178 }
4179
4180 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
4181 exit;
4182 }
4183 public function mxchat_handle_edit_intent() {
4184 // Security checks (nonce and permissions)
4185 if (!current_user_can('manage_options')) {
4186 wp_die(esc_html__('Unauthorized user', 'mxchat'));
4187 }
4188 check_admin_referer('mxchat_edit_intent');
4189
4190 // Get POST data
4191 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
4192 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
4193 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
4194 $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
4195 $similarity_threshold = min(95, max(10, $threshold_percentage)) / 100; // Convert to 0.10–0.95
4196
4197 // Handle enabled_bots
4198 $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
4199 $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
4200
4201 // Ensure default is always included for backward compatibility
4202 if (!in_array('default', $enabled_bots)) {
4203 $enabled_bots[] = 'default';
4204 }
4205
4206 $enabled_bots_json = json_encode($enabled_bots);
4207
4208 // Validate inputs
4209 if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
4210 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
4211 return;
4212 }
4213
4214 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
4215 if (empty($phrases_array)) {
4216 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
4217 return;
4218 }
4219
4220 // Generate embeddings with improved error handling
4221 $vectors = [];
4222 $failed_phrases = [];
4223
4224 foreach ($phrases_array as $phrase) {
4225 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
4226 if (is_array($embedding_vector)) {
4227 $vectors[] = $embedding_vector;
4228 } else {
4229 $failed_phrases[] = $phrase;
4230 }
4231 }
4232
4233 if (!empty($failed_phrases)) {
4234 $this->handle_embedding_error(
4235 sprintf(
4236 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
4237 implode(', ', $failed_phrases)
4238 )
4239 );
4240 return;
4241 }
4242
4243 if (empty($vectors)) {
4244 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
4245 return;
4246 }
4247
4248 $combined_vector = $this->mxchat_average_vectors($vectors);
4249 $serialized_vector = maybe_serialize($combined_vector);
4250
4251 // Update the database
4252 global $wpdb;
4253 $table_name = $wpdb->prefix . 'mxchat_intents';
4254
4255 $result = $wpdb->update(
4256 $table_name,
4257 array(
4258 'intent_label' => $intent_label,
4259 'phrases' => implode(', ', $phrases_array),
4260 'embedding_vector' => $serialized_vector,
4261 'similarity_threshold' => $similarity_threshold,
4262 'enabled_bots' => $enabled_bots_json // Include enabled_bots in update
4263 ),
4264 array('id' => $intent_id),
4265 array('%s', '%s', '%s', '%f', '%s'), // Format: string, string, string, float, string
4266 array('%d') // Where format: integer
4267 );
4268
4269 if (false === $result) {
4270 $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
4271 return;
4272 }
4273
4274 // Set success message and redirect
4275 set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
4276
4277 $redirect_url = add_query_arg(
4278 array(
4279 'page' => 'mxchat-actions'
4280 ),
4281 admin_url('admin.php')
4282 );
4283 wp_safe_redirect($redirect_url);
4284 exit;
4285 }
4286 public function mxchat_handle_add_intent() {
4287 if (!current_user_can('manage_options')) {
4288 wp_die(esc_html__('Unauthorized user', 'mxchat'));
4289 }
4290 check_admin_referer('mxchat_add_intent_nonce');
4291 global $wpdb;
4292 $table_name = $wpdb->prefix . 'mxchat_intents';
4293
4294 // Sanitize and get form data
4295 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
4296 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
4297 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
4298
4299 // Get similarity threshold from form (convert percentage to decimal)
4300 $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
4301
4302 // Handle enabled_bots
4303 $enabled_bots = isset($_POST['enabled_bots']) ? $_POST['enabled_bots'] : array('default');
4304 $enabled_bots = array_map('sanitize_text_field', $enabled_bots);
4305
4306 // Ensure default is always included for backward compatibility with existing actions
4307 if (!in_array('default', $enabled_bots)) {
4308 $enabled_bots[] = 'default';
4309 }
4310
4311 $enabled_bots_json = json_encode($enabled_bots);
4312
4313 // Validate required fields
4314 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
4315 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
4316 return;
4317 }
4318
4319 // Validate callback function
4320 $available_callbacks = $this->mxchat_get_available_callbacks();
4321 if (!array_key_exists($callback_function, $available_callbacks)) {
4322 $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
4323 return;
4324 }
4325
4326 // Check Pro requirements
4327 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
4328 if ($is_pro_only && !$this->is_activated) {
4329 $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
4330 return;
4331 }
4332
4333 // Process phrases
4334 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
4335 if (empty($phrases_array)) {
4336 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
4337 return;
4338 }
4339
4340 // Generate embeddings with improved error handling
4341 $vectors = [];
4342 $failed_phrases = [];
4343 foreach ($phrases_array as $phrase) {
4344 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
4345 if (is_array($embedding_vector)) {
4346 $vectors[] = $embedding_vector;
4347 } else {
4348 $failed_phrases[] = $phrase;
4349 }
4350 }
4351
4352 // Check for embedding failures
4353 if (!empty($failed_phrases)) {
4354 $this->handle_embedding_error(
4355 sprintf(
4356 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
4357 implode(', ', $failed_phrases)
4358 )
4359 );
4360 return;
4361 }
4362
4363 if (empty($vectors)) {
4364 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
4365 return;
4366 }
4367
4368 // Create combined vector and insert into database
4369 $combined_vector = $this->mxchat_average_vectors($vectors);
4370 $serialized_vector = maybe_serialize($combined_vector);
4371
4372 $result = $wpdb->insert($table_name, [
4373 'intent_label' => $intent_label,
4374 'phrases' => implode(', ', $phrases_array),
4375 'embedding_vector' => $serialized_vector,
4376 'callback_function' => $callback_function,
4377 'similarity_threshold' => $similarity_threshold,
4378 'enabled_bots' => $enabled_bots_json // NEW field
4379 ]);
4380
4381 if ($result === false) {
4382 $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
4383 return;
4384 }
4385
4386 // Set success message and redirect
4387 set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
4388 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
4389 exit;
4390 }
4391
4392
4393
4394
4395 private function handle_embedding_error($message, $redirect = true) {
4396 // Store the error message in the existing transient
4397 set_transient('mxchat_admin_notice_error', $message, 60);
4398
4399 if ($redirect) {
4400 // Redirect back to the actions page
4401 $redirect_url = add_query_arg(
4402 array(
4403 'page' => 'mxchat-actions'
4404 ),
4405 admin_url('admin.php')
4406 );
4407 wp_safe_redirect($redirect_url);
4408 exit;
4409 }
4410 }
4411
4412
4413 /**
4414 * Enhanced get_available_callbacks function with form action exclusion
4415 *
4416 * @param bool $grouped Whether to return callbacks grouped by category
4417 * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
4418 * @return array Callbacks data with icons, descriptions and availability status
4419 */
4420 private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
4421 // Load WordPress plugin functions if needed
4422 if (!function_exists('get_plugins')) {
4423 require_once ABSPATH . 'wp-admin/includes/plugin.php';
4424 }
4425
4426 // Get active plugins
4427 $active_plugins = get_option('active_plugins', array());
4428
4429 // Functions to exclude from the action selector only if Pro is activated
4430 // If user doesn't have Pro, show these so they can see what they're missing
4431 $excluded_when_pro_active_functions = array(
4432 'mxchat_handle_form_collection' // Forms add-on action
4433 );
4434
4435 // Always excluded functions (regardless of Pro status)
4436 $always_excluded_functions = array();
4437
4438 // Combine exclusion lists based on Pro activation status
4439 $excluded_functions = $always_excluded_functions;
4440 if ($this->is_activated) {
4441 // Only exclude add-on managed functions if Pro is active
4442 $excluded_functions = array_merge($excluded_functions, $excluded_when_pro_active_functions);
4443 }
4444
4445 // Define add-on plugin files and their corresponding action functions
4446 $addon_plugins = array(
4447 'mxchat-woo/mxchat-woo.php' => array(
4448 'functions' => array(
4449 'mxchat_handle_product_recommendations',
4450 'mxchat_handle_order_history',
4451 'mxchat_show_product_card',
4452 'mxchat_add_to_cart',
4453 'mxchat_checkout_redirect'
4454 ),
4455 'name' => __('WooCommerce Add-on', 'mxchat'),
4456 'pro_required' => true
4457 ),
4458 'mxchat-perplexity/mxchat-perplexity.php' => array(
4459 'functions' => array('mxchat_perplexity_research'),
4460 'name' => __('Perplexity Add-on', 'mxchat'),
4461 'pro_required' => true
4462 ),
4463 'mxchat-forms/mxchat-forms.php' => array(
4464 'functions' => array('mxchat_handle_form_collection'),
4465 'name' => __('Forms Add-on', 'mxchat'),
4466 'pro_required' => true
4467 ),
4468 // Add other add-ons and their functions here
4469 );
4470
4471 // Get the functions that are provided by active add-ons
4472 $addon_provided_functions = array();
4473 $addon_function_mapping = array(); // Maps functions to their add-on info
4474
4475 // Check which add-ons are active
4476 foreach ($addon_plugins as $plugin_file => $addon_info) {
4477 $is_active = in_array($plugin_file, $active_plugins);
4478
4479 // For each function in this addon
4480 foreach ($addon_info['functions'] as $function) {
4481 // Consider a function installed only if:
4482 // 1. The add-on is active AND
4483 // 2. Either it doesn't require Pro OR Pro is activated
4484 $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
4485
4486 // If the add-on is installed, mark this function as provided by an add-on
4487 if ($is_installed) {
4488 $addon_provided_functions[] = $function;
4489 }
4490
4491 // Store addon info for this function regardless of installation status
4492 $addon_function_mapping[$function] = array(
4493 'addon' => basename(dirname($plugin_file)),
4494 'addon_name' => $addon_info['name'],
4495 'pro_required' => $addon_info['pro_required'],
4496 'is_active' => $is_active,
4497 'is_installed' => $is_installed
4498 );
4499 }
4500 }
4501
4502 // Core callbacks - always available in the base plugin
4503 $core_callbacks = array(
4504 'mxchat_handle_email_capture' => array(
4505 'label' => __('Loops Email Capture', 'mxchat'),
4506 'pro_only' => false,
4507 'group' => __('Customer Engagement', 'mxchat'),
4508 'icon' => 'email-alt',
4509 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
4510 'addon' => false, // Not from an add-on
4511 'installed' => true // Always installed with base plugin
4512 ),
4513 'mxchat_handle_search_request' => array(
4514 'label' => __('Brave Web Search', 'mxchat'),
4515 'pro_only' => false,
4516 'group' => __('Search Features', 'mxchat'),
4517 'icon' => 'search',
4518 'description' => __('Let users search the web directly from the chat', 'mxchat'),
4519 'addon' => false,
4520 'installed' => true
4521 ),
4522 'mxchat_handle_image_search_request' => array(
4523 'label' => __('Brave Image Search', 'mxchat'),
4524 'pro_only' => false,
4525 'group' => __('Search Features', 'mxchat'),
4526 'icon' => 'format-image',
4527 'description' => __('Search and display images in the chat conversation', 'mxchat'),
4528 'addon' => false,
4529 'installed' => true
4530 ),
4531 // Pro core features - check is_activated property
4532 'mxchat_generate_image' => array(
4533 'label' => __('Generate Image', 'mxchat'),
4534 'pro_only' => false,
4535 'group' => __('Other Features', 'mxchat'),
4536 'icon' => 'art',
4537 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
4538 'addon' => false,
4539 'installed' => true
4540 ),
4541 'mxchat_handle_pdf_discussion' => array(
4542 'label' => __('Chat with PDF', 'mxchat'),
4543 'pro_only' => false,
4544 'group' => __('Other Features', 'mxchat'),
4545 'icon' => 'media-document',
4546 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
4547 'addon' => false,
4548 'installed' => true
4549 ),
4550 'mxchat_live_agent_handover' => array(
4551 'label' => __('Slack Live Agent', 'mxchat'),
4552 'pro_only' => false,
4553 'group' => __('Customer Engagement', 'mxchat'),
4554 'icon' => 'admin-users',
4555 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
4556 'addon' => false,
4557 'installed' => true
4558 ),
4559 'mxchat_handle_switch_to_chatbot_intent' => array(
4560 'label' => __('Back to Chatbot', 'mxchat'),
4561 'pro_only' => false,
4562 'group' => __('Customer Engagement', 'mxchat'),
4563 'icon' => 'backup',
4564 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
4565 'addon' => false,
4566 'installed' => true
4567 ),
4568 );
4569
4570 // Add-on callbacks with placeholders - only include if the add-on is NOT active
4571 $addon_callbacks = array(
4572 // WooCommerce Add-on
4573 'mxchat_handle_product_recommendations' => array(
4574 'label' => __('Product Recommendations', 'mxchat'),
4575 'pro_only' => true,
4576 'group' => __('WooCommerce Features', 'mxchat'),
4577 'icon' => 'cart',
4578 'description' => __('Suggest products based on customer preferences', 'mxchat'),
4579 ),
4580 'mxchat_handle_order_history' => array(
4581 'label' => __('Order History', 'mxchat'),
4582 'pro_only' => true,
4583 'group' => __('WooCommerce Features', 'mxchat'),
4584 'icon' => 'clipboard',
4585 'description' => __('Allow customers to check their order status', 'mxchat'),
4586 ),
4587 'mxchat_show_product_card' => array(
4588 'label' => __('Show Product Card', 'mxchat'),
4589 'pro_only' => true,
4590 'group' => __('WooCommerce Features', 'mxchat'),
4591 'icon' => 'products',
4592 'description' => __('Display product information in the chat', 'mxchat'),
4593 ),
4594 'mxchat_add_to_cart' => array(
4595 'label' => __('Add to Cart', 'mxchat'),
4596 'pro_only' => true,
4597 'group' => __('WooCommerce Features', 'mxchat'),
4598 'icon' => 'plus-alt',
4599 'description' => __('Add products to cart directly from chat', 'mxchat'),
4600 ),
4601 'mxchat_checkout_redirect' => array(
4602 'label' => __('Proceed to Checkout', 'mxchat'),
4603 'pro_only' => true,
4604 'group' => __('WooCommerce Features', 'mxchat'),
4605 'icon' => 'arrow-right-alt',
4606 'description' => __('Redirect customer to checkout page', 'mxchat'),
4607 ),
4608
4609 // Perplexity Add-on
4610 'mxchat_perplexity_research' => array(
4611 'label' => __('Perplexity Research', 'mxchat'),
4612 'pro_only' => true,
4613 'group' => __('Search Features', 'mxchat'),
4614 'icon' => 'book-alt',
4615 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
4616 ),
4617
4618 // Forms Add-on (only shown when Pro is not activated)
4619 'mxchat_handle_form_collection' => array(
4620 'label' => __('Form Collection', 'mxchat'),
4621 'pro_only' => true,
4622 'group' => __('Form Features', 'mxchat'),
4623 'icon' => 'feedback',
4624 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
4625 ),
4626 );
4627
4628 // Enhance add-on callbacks with installation status and addon info
4629 foreach ($addon_callbacks as $function => $data) {
4630 if (isset($addon_function_mapping[$function])) {
4631 $addon_info = $addon_function_mapping[$function];
4632
4633 $addon_callbacks[$function]['addon'] = $addon_info['addon'];
4634 $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4635 $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
4636
4637 // Set pro_only based on add-on configuration
4638 $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4639 } else {
4640 $addon_callbacks[$function]['addon'] = 'unknown';
4641 $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
4642 $addon_callbacks[$function]['installed'] = false;
4643 }
4644 }
4645
4646 // Initialize callbacks with core features
4647 $callbacks = $core_callbacks;
4648
4649 // Get callbacks from active add-ons
4650 $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
4651
4652 // Add placeholder callbacks only for add-ons that aren't active
4653 if ($include_all) {
4654 foreach ($addon_callbacks as $function => $data) {
4655 // Skip placeholders for functions provided by active add-ons
4656 if (in_array($function, $addon_provided_functions)) {
4657 continue;
4658 }
4659
4660 // Skip excluded functions
4661 if (in_array($function, $excluded_functions)) {
4662 continue;
4663 }
4664
4665 // Add the placeholder
4666 $callbacks[$function] = $data;
4667 }
4668 }
4669
4670 // Add callbacks from active add-ons (will override placeholders)
4671 foreach ($active_addon_callbacks as $function => $data) {
4672 // Skip excluded functions
4673 if (in_array($function, $excluded_functions)) {
4674 continue;
4675 }
4676
4677 // Always include callbacks from add-ons
4678 $callbacks[$function] = $data;
4679
4680 // Ensure they have the proper add-on info
4681 if (isset($addon_function_mapping[$function])) {
4682 $addon_info = $addon_function_mapping[$function];
4683 $callbacks[$function]['addon'] = $addon_info['addon'];
4684 $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
4685 $callbacks[$function]['installed'] = $addon_info['is_installed'];
4686 $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
4687 }
4688 }
4689
4690 // Just before returning callbacks, sort them to prioritize free features
4691 if (!$grouped) {
4692 // Create temporary arrays for sorting
4693 $free_callbacks = array();
4694 $pro_callbacks = array();
4695
4696 // Split callbacks into free and pro
4697 foreach ($callbacks as $key => $data) {
4698 if (isset($data['pro_only']) && $data['pro_only']) {
4699 $pro_callbacks[$key] = $data;
4700 } else {
4701 $free_callbacks[$key] = $data;
4702 }
4703 }
4704
4705 // Merge with free callbacks first
4706 $callbacks = array_merge($free_callbacks, $pro_callbacks);
4707 }
4708
4709 // Return grouped structure if requested
4710 if ($grouped) {
4711 $grouped_callbacks = array();
4712 foreach ($callbacks as $key => $data) {
4713 $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
4714
4715 // Ensure we carry forward all the new fields in grouped mode
4716 $callback_data = array(
4717 'label' => $data['label'],
4718 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
4719 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
4720 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
4721 'addon' => isset($data['addon']) ? $data['addon'] : false,
4722 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
4723 'installed' => isset($data['installed']) ? $data['installed'] : true
4724 );
4725
4726 $grouped_callbacks[$group_label][$key] = $callback_data;
4727 }
4728
4729 // Sort within each group to prioritize free features
4730 foreach ($grouped_callbacks as $group => $items) {
4731 $free_items = array();
4732 $pro_items = array();
4733
4734 foreach ($items as $key => $data) {
4735 if (isset($data['pro_only']) && $data['pro_only']) {
4736 $pro_items[$key] = $data;
4737 } else {
4738 $free_items[$key] = $data;
4739 }
4740 }
4741
4742 $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
4743 }
4744
4745 return $grouped_callbacks;
4746 }
4747
4748 return $callbacks;
4749 }
4750
4751 public function mxchat_page_init() {
4752 register_setting(
4753 'mxchat_option_group',
4754 'mxchat_options',
4755 array($this, 'mxchat_sanitize')
4756 );
4757
4758 register_setting(
4759 'mxchat_option_group',
4760 'mxchat_similarity_threshold',
4761 array(
4762 'type' => 'number',
4763 'sanitize_callback' => function($value) {
4764 $value = absint($value);
4765 return min(max($value, 20), 95);
4766 },
4767 'default' => 80,
4768 )
4769 );
4770
4771 // Chatbot Settings Section
4772 add_settings_section(
4773 'mxchat_chatbot_section',
4774 esc_html__('Chatbot Settings', 'mxchat'),
4775 null,
4776 'mxchat-chatbot'
4777 );
4778
4779 // API Keys Settings Section
4780 add_settings_section(
4781 'mxchat_api_keys_section',
4782 esc_html__('API Keys', 'mxchat'),
4783 array($this, 'mxchat_api_keys_section_callback'),
4784 'mxchat-api-keys'
4785 );
4786
4787 // OpenAI API Key
4788 add_settings_field(
4789 'api_key',
4790 esc_html__('OpenAI API Key', 'mxchat'),
4791 array($this, 'api_key_callback'),
4792 'mxchat-api-keys',
4793 'mxchat_api_keys_section'
4794 );
4795
4796 // X.AI API Key
4797 add_settings_field(
4798 'xai_api_key',
4799 esc_html__('X.AI API Key', 'mxchat'),
4800 array($this, 'xai_api_key_callback'),
4801 'mxchat-api-keys',
4802 'mxchat_api_keys_section'
4803 );
4804
4805 // Claude API Key
4806 add_settings_field(
4807 'claude_api_key',
4808 esc_html__('Claude API Key', 'mxchat'),
4809 array($this, 'claude_api_key_callback'),
4810 'mxchat-api-keys',
4811 'mxchat_api_keys_section'
4812 );
4813
4814 // DeepSeek API Key
4815 add_settings_field(
4816 'deepseek_api_key',
4817 esc_html__('DeepSeek API Key', 'mxchat'),
4818 array($this, 'deepseek_api_key_callback'),
4819 'mxchat-api-keys',
4820 'mxchat_api_keys_section'
4821 );
4822
4823 // Google Gemini API Key
4824 add_settings_field(
4825 'gemini_api_key',
4826 esc_html__('Google Gemini API Key', 'mxchat'),
4827 array($this, 'gemini_api_key_callback'),
4828 'mxchat-api-keys',
4829 'mxchat_api_keys_section'
4830 );
4831
4832 // Voyage AI API Key
4833 add_settings_field(
4834 'voyage_api_key',
4835 esc_html__('Voyage AI API Key', 'mxchat'),
4836 array($this, 'voyage_api_key_callback'),
4837 'mxchat-api-keys',
4838 'mxchat_api_keys_section'
4839 );
4840
4841 // OpenRouter API Key
4842 add_settings_field(
4843 'openrouter_api_key',
4844 esc_html__('OpenRouter API Key', 'mxchat'),
4845 array($this, 'openrouter_api_key_callback'),
4846 'mxchat-api-keys',
4847 'mxchat_api_keys_section'
4848 );
4849
4850 // Loops API Key
4851 add_settings_field(
4852 'loops_api_key',
4853 esc_html__('Loops API Key', 'mxchat'),
4854 array($this, 'mxchat_loops_api_key_callback'),
4855 'mxchat-api-keys',
4856 'mxchat_api_keys_section'
4857 );
4858
4859 // Brave Search API Key
4860 add_settings_field(
4861 'brave_api_key',
4862 __('Brave API Key', 'mxchat'),
4863 array($this, 'mxchat_brave_api_key_callback'),
4864 'mxchat-api-keys',
4865 'mxchat_api_keys_section'
4866 );
4867
4868 // Similarity Threshold Slider
4869 add_settings_field(
4870 'similarity_threshold', // Field ID
4871 esc_html__('Similarity Threshold', 'mxchat'), // Field title
4872 array($this, 'mxchat_similarity_threshold_callback'), // Callback function
4873 'mxchat-chatbot', // Page
4874 'mxchat_chatbot_section' // Section
4875 );
4876
4877 add_settings_field(
4878 'append_to_body',
4879 esc_html__('Auto-Display Chatbot', 'mxchat'),
4880 array($this, 'mxchat_append_to_body_callback'),
4881 'mxchat-chatbot',
4882 'mxchat_chatbot_section'
4883 );
4884
4885 add_settings_field(
4886 'contextual_awareness_toggle',
4887 esc_html__('Contextual Awareness', 'mxchat'),
4888 array($this, 'mxchat_contextual_awareness_callback'),
4889 'mxchat-chatbot',
4890 'mxchat_chatbot_section'
4891 );
4892
4893 add_settings_field(
4894 'enable_streaming_toggle',
4895 esc_html__('Enable Streaming', 'mxchat'),
4896 array($this, 'enable_streaming_toggle_callback'),
4897 'mxchat-chatbot',
4898 'mxchat_chatbot_section', // Same section as your working toggle
4899 array(
4900 'class' => 'mxchat-setting-row streaming-setting',
4901 'style' => 'display: none;' // Hidden by default, shown when OpenAI/Claude selected
4902 )
4903 );
4904
4905
4906 add_settings_field(
4907 'model',
4908 esc_html__('Chat Model', 'mxchat'),
4909 array($this, 'mxchat_model_callback'),
4910 'mxchat-chatbot',
4911 'mxchat_chatbot_section'
4912 );
4913
4914 add_settings_field(
4915 'embedding_model',
4916 esc_html__('Embedding Model', 'mxchat'),
4917 array($this, 'embedding_model_callback'),
4918 'mxchat-chatbot',
4919 'mxchat_chatbot_section'
4920 );
4921
4922 add_settings_field(
4923 'system_prompt_instructions',
4924 esc_html__('AI Instructions (Behavior)', 'mxchat'),
4925 array($this, 'system_prompt_instructions_callback'),
4926 'mxchat-chatbot',
4927 'mxchat_chatbot_section'
4928 );
4929
4930
4931 add_settings_field(
4932 'top_bar_title',
4933 esc_html__('Top Bar Title', 'mxchat'),
4934 array($this, 'mxchat_top_bar_title_callback'),
4935 'mxchat-chatbot',
4936 'mxchat_chatbot_section'
4937 );
4938
4939 add_settings_field(
4940 'ai_agent_text',
4941 esc_html__('AI Agent Text', 'mxchat'),
4942 array($this, 'mxchat_ai_agent_text_callback'),
4943 'mxchat-chatbot',
4944 'mxchat_chatbot_section'
4945 );
4946
4947 add_settings_field(
4948 'enable_email_block',
4949 esc_html__('Require Email To Chat', 'mxchat'),
4950 array($this, 'enable_email_block_callback'),
4951 'mxchat-chatbot',
4952 'mxchat_chatbot_section'
4953 );
4954
4955 add_settings_field(
4956 'email_blocker_header_content',
4957 esc_html__('Require Email Chat Content', 'mxchat'),
4958 array($this, 'email_blocker_header_content_callback'),
4959 'mxchat-chatbot',
4960 'mxchat_chatbot_section'
4961 );
4962
4963 add_settings_field(
4964 'email_blocker_button_text',
4965 esc_html__('Require Email Chat Button Text', 'mxchat'),
4966 [$this, 'email_blocker_button_text_callback'],
4967 'mxchat-chatbot',
4968 'mxchat_chatbot_section'
4969 );
4970
4971 add_settings_field(
4972 'enable_name_field',
4973 esc_html__('Require Name Field', 'mxchat'),
4974 array($this, 'enable_name_field_callback'),
4975 'mxchat-chatbot',
4976 'mxchat_chatbot_section'
4977 );
4978
4979 add_settings_field(
4980 'name_field_placeholder',
4981 esc_html__('Name Field Placeholder', 'mxchat'),
4982 array($this, 'name_field_placeholder_callback'),
4983 'mxchat-chatbot',
4984 'mxchat_chatbot_section'
4985 );
4986
4987 add_settings_field(
4988 'intro_message',
4989 esc_html__('Introductory Message', 'mxchat'),
4990 array($this, 'mxchat_intro_message_callback'),
4991 'mxchat-chatbot',
4992 'mxchat_chatbot_section'
4993 );
4994
4995 add_settings_field(
4996 'input_copy',
4997 esc_html__('Input Copy', 'mxchat'),
4998 array($this, 'mxchat_input_copy_callback'),
4999 'mxchat-chatbot',
5000 'mxchat_chatbot_section'
5001 );
5002
5003 add_settings_field(
5004 'pre_chat_message',
5005 esc_html__('Chat Teaser Pop-up', 'mxchat'),
5006 array($this, 'mxchat_pre_chat_message_callback'),
5007 'mxchat-chatbot',
5008 'mxchat_chatbot_section'
5009 );
5010
5011 add_settings_field(
5012 'privacy_toggle',
5013 esc_html__('Toggle Privacy Notice', 'mxchat'),
5014 array($this, 'mxchat_privacy_toggle_callback'),
5015 'mxchat-chatbot',
5016 'mxchat_chatbot_section'
5017 );
5018
5019 add_settings_field(
5020 'complianz_toggle',
5021 esc_html__('Enable Complianz', 'mxchat'),
5022 array($this, 'mxchat_complianz_toggle_callback'),
5023 'mxchat-chatbot',
5024 'mxchat_chatbot_section'
5025 );
5026
5027 add_settings_field(
5028 'link_target_toggle',
5029 esc_html__('Open Links in a New Tab', 'mxchat'),
5030 array($this, 'mxchat_link_target_toggle_callback'),
5031 'mxchat-chatbot',
5032 'mxchat_chatbot_section'
5033 );
5034
5035 add_settings_field(
5036 'chat_persistence_toggle',
5037 esc_html__('Enable Chat Persistence', 'mxchat'),
5038 array($this, 'mxchat_chat_persistence_toggle_callback'),
5039 'mxchat-chatbot',
5040 'mxchat_chatbot_section'
5041 );
5042
5043 add_settings_field(
5044 'popular_question_1',
5045 esc_html__('Quick Question 1', 'mxchat'),
5046 array($this, 'mxchat_popular_question_1_callback'),
5047 'mxchat-chatbot',
5048 'mxchat_chatbot_section'
5049 );
5050
5051 add_settings_field(
5052 'popular_question_2',
5053 esc_html__('Quick Question 2', 'mxchat'),
5054 array($this, 'mxchat_popular_question_2_callback'),
5055 'mxchat-chatbot',
5056 'mxchat_chatbot_section'
5057 );
5058
5059 add_settings_field(
5060 'popular_question_3',
5061 esc_html__('Quick Question 3', 'mxchat'),
5062 array($this, 'mxchat_popular_question_3_callback'),
5063 'mxchat-chatbot',
5064 'mxchat_chatbot_section'
5065 );
5066
5067 add_settings_field(
5068 'additional_popular_questions',
5069 esc_html__('Additional Quick Questions', 'mxchat'),
5070 array($this, 'mxchat_additional_popular_questions_callback'),
5071 'mxchat-chatbot',
5072 'mxchat_chatbot_section'
5073 );
5074
5075
5076 add_settings_field(
5077 'rate_limits',
5078 __('Rate Limits Settings', 'mxchat'),
5079 array($this, 'mxchat_rate_limits_callback'),
5080 'mxchat-chatbot',
5081 'mxchat_chatbot_section'
5082 );
5083
5084 // Loops Settings Section
5085 add_settings_section(
5086 'mxchat_loops_section',
5087 esc_html__('Loops Settings', 'mxchat'),
5088 null,
5089 'mxchat-embed'
5090 );
5091
5092 // Loops Settings Fields (API Key moved to API Keys tab)
5093 add_settings_field(
5094 'loops_mailing_list',
5095 esc_html__('Loops Mailing List', 'mxchat'),
5096 array($this, 'mxchat_loops_mailing_list_callback'),
5097 'mxchat-embed',
5098 'mxchat_loops_section'
5099 );
5100
5101 add_settings_field(
5102 'triggered_phrase_response',
5103 esc_html__('Triggered Phrase Response', 'mxchat'),
5104 array($this, 'mxchat_triggered_phrase_response_callback'),
5105 'mxchat-embed',
5106 'mxchat_loops_section'
5107 );
5108
5109 add_settings_field(
5110 'email_capture_response',
5111 esc_html__('Email Capture Response', 'mxchat'),
5112 array($this, 'mxchat_email_capture_response_callback'),
5113 'mxchat-embed',
5114 'mxchat_loops_section'
5115 );
5116
5117 // Brave Search Settings Fields
5118 add_settings_section(
5119 'mxchat_brave_section',
5120 __('Brave Search Settings', 'mxchat'),
5121 array($this, 'mxchat_brave_section_callback'),
5122 'mxchat-embed'
5123 );
5124
5125 // Brave API Key moved to API Keys tab
5126 add_settings_field(
5127 'brave_image_count',
5128 __('Number of Images to Return', 'mxchat'),
5129 array($this, 'mxchat_brave_image_count_callback'),
5130 'mxchat-embed',
5131 'mxchat_brave_section'
5132 );
5133
5134 add_settings_field(
5135 'brave_safe_search',
5136 __('Safe Search', 'mxchat'),
5137 array($this, 'mxchat_brave_safe_search_callback'),
5138 'mxchat-embed',
5139 'mxchat_brave_section'
5140 );
5141
5142 add_settings_field(
5143 'brave_news_count',
5144 __('Number of News Articles', 'mxchat'),
5145 array($this, 'mxchat_brave_news_count_callback'),
5146 'mxchat-embed',
5147 'mxchat_brave_section'
5148 );
5149
5150 add_settings_field(
5151 'brave_country',
5152 __('Country', 'mxchat'),
5153 array($this, 'mxchat_brave_country_callback'),
5154 'mxchat-embed',
5155 'mxchat_brave_section'
5156 );
5157
5158 add_settings_field(
5159 'brave_language',
5160 __('Language', 'mxchat'),
5161 array($this, 'mxchat_brave_language_callback'),
5162 'mxchat-embed',
5163 'mxchat_brave_section'
5164 );
5165
5166 // Chat with PDF Intent Settings Fields
5167 add_settings_section(
5168 'mxchat_pdf_intent_section',
5169 __('Toolbar Settings & Intents', 'mxchat'),
5170 array($this, 'mxchat_pdf_intent_section_callback'),
5171 'mxchat-embed'
5172 );
5173
5174 add_settings_field(
5175 'chat_toolbar_toggle',
5176 __('Show Chat Toolbar', 'mxchat'),
5177 array($this, 'mxchat_chat_toolbar_toggle_callback'),
5178 'mxchat-embed',
5179 'mxchat_pdf_intent_section'
5180 );
5181
5182 // PDF Upload Button Toggle
5183 add_settings_field(
5184 'show_pdf_upload_button',
5185 __('Show PDF Upload Button', 'mxchat'),
5186 array($this, 'mxchat_show_pdf_upload_button_callback'),
5187 'mxchat-embed',
5188 'mxchat_pdf_intent_section'
5189 );
5190
5191 // Word Upload Button Toggle
5192 add_settings_field(
5193 'show_word_upload_button',
5194 __('Show Word Upload Button', 'mxchat'),
5195 array($this, 'mxchat_show_word_upload_button_callback'),
5196 'mxchat-embed',
5197 'mxchat_pdf_intent_section'
5198 );
5199
5200 add_settings_field(
5201 'pdf_intent_trigger_text',
5202 __('Intent Trigger Text', 'mxchat'),
5203 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
5204 'mxchat-embed',
5205 'mxchat_pdf_intent_section'
5206 );
5207
5208 add_settings_field(
5209 'pdf_intent_success_text',
5210 __('Success Text', 'mxchat'),
5211 array($this, 'mxchat_pdf_intent_success_text_callback'),
5212 'mxchat-embed',
5213 'mxchat_pdf_intent_section'
5214 );
5215
5216 add_settings_field(
5217 'pdf_intent_error_text',
5218 __('Error Text', 'mxchat'),
5219 array($this, 'mxchat_pdf_intent_error_text_callback'),
5220 'mxchat-embed',
5221 'mxchat_pdf_intent_section'
5222 );
5223
5224 // Add PDF Maximum Pages Field
5225 add_settings_field(
5226 'pdf_max_pages',
5227 __('Maximum Document Pages', 'mxchat'),
5228 array($this, 'mxchat_pdf_max_pages_callback'),
5229 'mxchat-embed',
5230 'mxchat_pdf_intent_section'
5231 );
5232
5233 // Live Agent Settings Fields
5234 add_settings_section(
5235 'mxchat_live_agent_section',
5236 __('Live Agent Settings', 'mxchat'),
5237 array($this, 'mxchat_live_agent_section_callback'),
5238 'mxchat-embed'
5239 );
5240
5241 // Live Agent Status Fields (add at top of live agent settings)
5242 add_settings_field(
5243 'live_agent_status',
5244 __('Live Agent Status', 'mxchat'),
5245 array($this, 'mxchat_live_agent_status_callback'),
5246 'mxchat-embed',
5247 'mxchat_live_agent_section'
5248 );
5249
5250 add_settings_field(
5251 'live_agent_notification_message',
5252 __('Notification Message', 'mxchat'),
5253 array($this, 'mxchat_live_agent_notification_message_callback'),
5254 'mxchat-embed',
5255 'mxchat_live_agent_section'
5256 );
5257
5258 add_settings_field(
5259 'live_agent_away_message',
5260 __('Away Message', 'mxchat'),
5261 array($this, 'mxchat_live_agent_away_message_callback'),
5262 'mxchat-embed',
5263 'mxchat_live_agent_section'
5264 );
5265
5266 add_settings_field(
5267 'live_agent_user_ids',
5268 __('Slack Agent User IDs', 'mxchat'),
5269 array($this, 'mxchat_live_agent_user_ids_callback'),
5270 'mxchat-embed',
5271 'mxchat_live_agent_section'
5272 );
5273
5274 add_settings_field(
5275 'live_agent_webhook_url',
5276 __('Slack Webhook URL', 'mxchat'),
5277 array($this, 'mxchat_live_agent_webhook_url_callback'),
5278 'mxchat-embed',
5279 'mxchat_live_agent_section'
5280 );
5281
5282 add_settings_field(
5283 'live_agent_secret_key',
5284 __('Slack Secret Key', 'mxchat'),
5285 array($this, 'mxchat_live_agent_secret_key_callback'),
5286 'mxchat-embed',
5287 'mxchat_live_agent_section'
5288 );
5289
5290 // Live Agent Integration Fields
5291 add_settings_field(
5292 'live_agent_bot_token',
5293 __('Slack Bot OAuth Token', 'mxchat'),
5294 array($this, 'mxchat_live_agent_bot_token_callback'),
5295 'mxchat-embed',
5296 'mxchat_live_agent_section'
5297 );
5298
5299
5300
5301
5302 // General Settings Section
5303 add_settings_section(
5304 'mxchat_general_section',
5305 esc_html__('YouTube Tutorials', 'mxchat'),
5306 null,
5307 'mxchat-general'
5308 );
5309 }
5310
5311 public function mxchat_prompts_page_init() {
5312 register_setting(
5313 'mxchat_prompts_options',
5314 'mxchat_prompts_options',
5315 array(
5316 'type' => 'array',
5317 'description' => __('MXChat Knowledge Base Settings', 'mxchat'),
5318 'default' => array(
5319 'mxchat_auto_sync_posts' => 0,
5320 'mxchat_auto_sync_pages' => 0,
5321 'mxchat_use_pinecone' => 0,
5322 'mxchat_pinecone_api_key' => '',
5323 'mxchat_pinecone_environment' => '',
5324 'mxchat_pinecone_index' => '',
5325 'mxchat_pinecone_host' => '',
5326 ),
5327 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
5328 )
5329 );
5330
5331 add_action('admin_notices', array($this, 'sync_settings_notice'));
5332 }
5333
5334 public function mxchat_transcripts_page_init() {
5335 register_setting(
5336 'mxchat_transcripts_options',
5337 'mxchat_transcripts_options',
5338 array(
5339 'type' => 'array',
5340 'description' => __('MXChat Transcripts Notification Settings', 'mxchat'),
5341 'default' => array(
5342 'mxchat_enable_notifications' => 0,
5343 'mxchat_notification_email' => get_option('admin_email'),
5344 'mxchat_auto_delete_transcripts' => 'never',
5345 ),
5346 'sanitize_callback' => array($this, 'sanitize_transcripts_options'),
5347 )
5348 );
5349
5350 add_settings_section(
5351 'mxchat_transcripts_notification_section',
5352 esc_html__('Chat Notification Settings', 'mxchat'),
5353 array($this, 'mxchat_transcripts_notification_section_callback'),
5354 'mxchat-transcripts'
5355 );
5356
5357 add_settings_field(
5358 'mxchat_enable_notifications',
5359 esc_html__('Enable Chat Notifications', 'mxchat'),
5360 array($this, 'mxchat_enable_notifications_callback'),
5361 'mxchat-transcripts',
5362 'mxchat_transcripts_notification_section'
5363 );
5364
5365 add_settings_field(
5366 'mxchat_notification_email',
5367 esc_html__('Notification Email Address', 'mxchat'),
5368 array($this, 'mxchat_notification_email_callback'),
5369 'mxchat-transcripts',
5370 'mxchat_transcripts_notification_section'
5371 );
5372
5373 add_settings_field(
5374 'mxchat_auto_delete_transcripts',
5375 esc_html__('Auto-Delete Old Transcripts', 'mxchat'),
5376 array($this, 'mxchat_auto_delete_transcripts_callback'),
5377 'mxchat-transcripts',
5378 'mxchat_transcripts_notification_section'
5379 );
5380
5381 add_settings_field(
5382 'mxchat_auto_email_transcript',
5383 esc_html__('Auto-Email Full Transcript', 'mxchat'),
5384 array($this, 'mxchat_auto_email_transcript_callback'),
5385 'mxchat-transcripts',
5386 'mxchat_transcripts_notification_section'
5387 );
5388 }
5389
5390
5391 /**
5392 * Sanitize all prompts options
5393 *
5394 * @param array $input The unsanitized options array
5395 * @return array The sanitized options array
5396 */
5397 public function sanitize_prompts_options($input) {
5398 // Log the incoming input.
5399 //error_log('Sanitizing inputs: ' . print_r($input, true));
5400
5401 $sanitized = array();
5402
5403 // Boolean options
5404 $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0;
5405 $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0;
5406 $sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0;
5407
5408 // API Key: if less than 32 characters, flag as invalid.
5409 $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
5410 if (!empty($api_key) && strlen($api_key) < 32) {
5411 add_settings_error(
5412 'mxchat_prompts_options',
5413 'invalid_api_key',
5414 __('The Pinecone API key appears to be invalid. Please check your API key.', 'mxchat')
5415 );
5416 $existing_options = get_option('mxchat_prompts_options', array());
5417 $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? '';
5418 } else {
5419 $sanitized['mxchat_pinecone_api_key'] = $api_key;
5420 }
5421
5422 // Environment and Index Name
5423 $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
5424 $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
5425
5426 // Host: Remove protocol and validate format.
5427 $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
5428 $host = preg_replace('#^https?://#', '', $host);
5429 //error_log('Host after removing protocol: ' . $host);
5430 if (!empty($host)) {
5431 if (!preg_match('/^[\w-]+\.svc\.[\w-]+\.pinecone\.io$/', $host)) {
5432 add_settings_error(
5433 'mxchat_prompts_options',
5434 'invalid_host',
5435 __('The Pinecone host appears to be invalid. It should look like "mxchat-vectors-zrmsquq.svc.aped-4627-b74a.pinecone.io"', 'mxchat')
5436 );
5437 $existing_options = get_option('mxchat_prompts_options', array());
5438 $sanitized['mxchat_pinecone_host'] = $existing_options['mxchat_pinecone_host'] ?? '';
5439 } else {
5440 $sanitized['mxchat_pinecone_host'] = $host;
5441 }
5442 } else {
5443 $sanitized['mxchat_pinecone_host'] = '';
5444 }
5445
5446 //error_log('Final sanitized array: ' . print_r($sanitized, true));
5447
5448 return $sanitized;
5449 }
5450
5451 public function sync_settings_notice() {
5452 // Only show notice on our plugin page
5453 if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
5454 return;
5455 }
5456
5457 // Check if settings were updated
5458 if (isset($_GET['settings-updated'])) {
5459
5460 ?>
5461 <div class="notice notice-success is-dismissible">
5462 <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
5463 </div>
5464 <?php
5465
5466 }
5467 }
5468 // Add this sanitization function to your class
5469 public function sanitize_sync_setting($input) {
5470 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
5471 }
5472
5473 public function mxchat_rate_limits_callback() {
5474 $all_options = get_option('mxchat_options', []);
5475
5476 // Define available rate limits
5477 $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
5478
5479 // Define available timeframes
5480 $timeframes = array(
5481 'hourly' => __('Per Hour', 'mxchat'),
5482 'daily' => __('Per Day', 'mxchat'),
5483 'weekly' => __('Per Week', 'mxchat'),
5484 'monthly' => __('Per Month', 'mxchat')
5485 );
5486
5487 // Get all roles plus a "logged_out" pseudo-role
5488 $roles = wp_roles()->get_names();
5489 $roles['logged_out'] = __('Logged Out Users', 'mxchat');
5490
5491 // Start the wrapper
5492 echo '<div class="pro-feature-wrapper active">';
5493 echo '<div class="mxchat-rate-limits-container">';
5494
5495 echo '<p class="description" style="margin-bottom: 20px;">' .
5496 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') .
5497 '</p>';
5498
5499 // Add markdown link documentation
5500 echo '<div class="notice notice-info inline" style="margin-bottom: 20px; padding: 10px;">';
5501 echo '<p><strong>' . esc_html__('Markdown Links Supported:', 'mxchat') . '</strong></p>';
5502 echo '<p>' . esc_html__('You can include clickable links in your custom messages using markdown syntax:', 'mxchat') . '</p>';
5503 echo '<ul style="margin-left: 20px;">';
5504 echo '<li><code>[Link text](https://example.com)</code> - Creates a clickable link</li>';
5505 echo '<li><code>[Visit our pricing](https://example.com/pricing)</code> - Link with custom text</li>';
5506 echo '<li><code>Plain URLs like https://example.com will also become clickable</code></li>';
5507 echo '</ul>';
5508 echo '</div>';
5509
5510 // Output the controls for each role
5511 foreach ($roles as $role_id => $role_name) {
5512 // Get saved options or defaults
5513 $default_limit = ($role_id === 'logged_out') ? '10' : '100';
5514 $default_timeframe = 'daily';
5515 $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
5516
5517 $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
5518 ? $all_options['rate_limits'][$role_id]['limit']
5519 : $default_limit;
5520
5521 $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
5522 ? $all_options['rate_limits'][$role_id]['timeframe']
5523 : $default_timeframe;
5524
5525 $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
5526 ? $all_options['rate_limits'][$role_id]['message']
5527 : $default_message;
5528
5529 // Output the row
5530 echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
5531
5532 // Role label
5533 echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
5534
5535 // Controls section
5536 echo '<div class="mxchat-rate-limit-controls-wrapper">';
5537
5538 // Rate limit and timeframe controls
5539 echo '<div class="mxchat-rate-limit-controls">';
5540
5541 // Limit dropdown
5542 echo '<div>';
5543 echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
5544 echo '<select
5545 id="rate_limits_' . esc_attr($role_id) . '_limit"
5546 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
5547 class="mxchat-autosave-field">';
5548 foreach ($rate_limits as $limit) {
5549 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
5550 }
5551 echo '</select>';
5552 echo '</div>';
5553
5554 // Timeframe dropdown
5555 echo '<div>';
5556 echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
5557 echo '<select
5558 id="rate_limits_' . esc_attr($role_id) . '_timeframe"
5559 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
5560 class="mxchat-autosave-field">';
5561 foreach ($timeframes as $value => $label) {
5562 echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
5563 }
5564 echo '</select>';
5565 echo '</div>';
5566
5567 echo '</div>'; // End controls
5568
5569 // Custom message textarea
5570 echo '<div class="mxchat-rate-limit-message">';
5571 echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
5572 echo '<textarea
5573 id="rate_limits_' . esc_attr($role_id) . '_message"
5574 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
5575 class="mxchat-autosave-field"
5576 placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
5577 esc_textarea($custom_message) .
5578 '</textarea>';
5579 echo '<p class="description">' .
5580 esc_html__('Example: Rate limit reached! [Visit our pricing page](https://example.com/pricing) to upgrade.', 'mxchat') .
5581 '</p>';
5582 echo '</div>'; // End message
5583
5584 echo '</div>'; // End controls wrapper
5585
5586 echo '</div>'; // End row
5587 }
5588
5589 echo '</div>'; // End container
5590
5591 echo '</div>'; // End pro-feature-wrapper
5592 }
5593
5594 private function mxchat_add_option_field($id, $title, $callback = '') {
5595 add_settings_field(
5596 $id,
5597 __($title, 'mxchat'),
5598 $callback ? array($this, $callback) : array($this, $id . '_callback'),
5599 'mxchat-max',
5600 'mxchat_setting_section_id',
5601 $id === 'model' ? ['label_for' => 'model'] : []
5602 );
5603 }
5604
5605 // API Keys Section Callback
5606 public function mxchat_api_keys_section_callback() {
5607 echo '<p>' . esc_html__('Manage all your API keys in one place. Add the API keys for the services you want to use with your chatbot.', 'mxchat') . '</p>';
5608 }
5609
5610 // OpenAI API Key
5611 public function api_key_callback() {
5612 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
5613
5614 echo '<div class="api-key-wrapper">';
5615 echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5616 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5617 echo '<p class="description">' . esc_html__('Required for OpenAI GPT models and OpenAI embeddings. Get your API key from OpenAI Platform.', 'mxchat') . '</p>';
5618 echo '</div>';
5619 }
5620
5621 // X.AI API Key
5622 public function xai_api_key_callback() {
5623 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
5624
5625 echo '<div class="api-key-wrapper">';
5626 echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
5627 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5628 echo '<p class="description">' . esc_html__('Required for X.AI Grok models. Get your API key from X.AI Console.', 'mxchat') . '</p>';
5629 echo '</div>';
5630 }
5631 // Claude API Key
5632 public function claude_api_key_callback() {
5633 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
5634
5635 echo '<div class="api-key-wrapper">';
5636 echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
5637 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5638 echo '<p class="description">' . esc_html__('Required for Anthropic Claude models. Get your API key from Anthropic Console.', 'mxchat') . '</p>';
5639 echo '</div>';
5640 }
5641
5642 // DeepSeek API Key
5643 public function deepseek_api_key_callback() {
5644 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
5645
5646 echo '<div class="api-key-wrapper">';
5647 echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5648 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5649 echo '<p class="description">' . esc_html__('Required for DeepSeek models. Get your API key from DeepSeek Platform.', 'mxchat') . '</p>';
5650 echo '</div>';
5651 }
5652
5653 // Gemini API Key
5654 public function gemini_api_key_callback() {
5655 $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
5656
5657 echo '<div class="api-key-wrapper">';
5658 echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
5659 echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5660 echo '<p class="description">' . esc_html__('Required for Google Gemini models and embeddings. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
5661 echo '</div>';
5662 }
5663
5664
5665 // OpenRouter API Key
5666 public function openrouter_api_key_callback() {
5667 $openrouterApiKey = isset($this->options['openrouter_api_key']) ? esc_attr($this->options['openrouter_api_key']) : '';
5668 echo '<div class="api-key-wrapper">';
5669 echo '<input type="password" id="openrouter_api_key" name="openrouter_api_key" value="' . $openrouterApiKey . '" class="regular-text" autocomplete="off" />';
5670 echo '<button type="button" id="toggleOpenRouterApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5671 echo '<p class="description">' . esc_html__('Required for OpenRouter models. Get your API key from OpenRouter.ai', 'mxchat') . '</p>';
5672 echo '</div>';
5673 }
5674
5675 // Voyage API Key
5676 public function voyage_api_key_callback() {
5677 $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
5678
5679 echo '<div class="api-key-wrapper">';
5680 echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
5681 echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5682 echo '<p class="description">' . esc_html__('Required for Voyage AI embedding models. Get your API key from Voyage AI.', 'mxchat') . '</p>';
5683 echo '</div>';
5684 }
5685
5686 public function mxchat_loops_api_key_callback() {
5687 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
5688
5689 // Hidden fields to "trap" autofill
5690 echo '<input type="text" style="display:none" autocomplete="username" />';
5691 echo '<input type="password" style="display:none" autocomplete="current-password" />';
5692
5693 echo '<div class="api-key-wrapper">';
5694 echo sprintf(
5695 '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
5696 $loops_api_key
5697 );
5698 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5699 echo '</div>';
5700 echo '<p class="description">' . esc_html__('Required for Loops email integration. Get your API key from Loops.so', 'mxchat') . '</p>';
5701 }
5702 public function mxchat_loops_mailing_list_callback() {
5703 // Add error handling and type checking
5704 $loops_api_key = '';
5705 $selected_list = '';
5706
5707 // Safely get the API key
5708 if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
5709 $loops_api_key = $this->options['loops_api_key'];
5710 }
5711
5712 // Safely get the selected list
5713 if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
5714 $selected_list = $this->options['loops_mailing_list'];
5715 }
5716
5717 if (!empty($loops_api_key)) {
5718 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
5719 if (is_array($lists) && !empty($lists)) {
5720 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
5721
5722 // Add a default "Select a list" option
5723 echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
5724
5725 foreach ($lists as $list) {
5726 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
5727 echo sprintf(
5728 '<option value="%s" %s>%s</option>',
5729 esc_attr($list['id']),
5730 selected($selected_list, $list['id'], false),
5731 esc_html($list['name'])
5732 );
5733 }
5734 }
5735 echo '</select>';
5736 echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
5737 } else {
5738 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
5739 }
5740 } else {
5741 echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
5742 }
5743 }
5744 public function mxchat_triggered_phrase_response_callback() {
5745 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
5746 $triggered_response = isset($this->options['triggered_phrase_response'])
5747 ? $this->options['triggered_phrase_response']
5748 : $default_response;
5749 echo sprintf(
5750 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
5751 esc_textarea($triggered_response)
5752 );
5753 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>';
5754 }
5755
5756 public function mxchat_email_capture_response_callback() {
5757 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
5758 $email_capture_response = isset($this->options['email_capture_response'])
5759 ? $this->options['email_capture_response']
5760 : $default_response;
5761 echo sprintf(
5762 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
5763 esc_textarea($email_capture_response)
5764 );
5765 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>';
5766 }
5767 public function mxchat_pre_chat_message_callback() {
5768 // Load the entire 'mxchat_options' array
5769 $all_options = get_option('mxchat_options', []);
5770
5771 // Retrieve the saved message or use the default value
5772 $default_message = __('Hey there! Ask me anything!', 'mxchat');
5773 $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
5774
5775 // Output the textarea
5776 printf(
5777 '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
5778 esc_textarea($pre_chat_message)
5779 );
5780 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>';
5781 }
5782
5783 // Callback for AI Instructions textarea
5784 public function system_prompt_instructions_callback() {
5785 // Retrieve the current value of the system prompt instructions
5786 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
5787 // Render the textarea field
5788 printf(
5789 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
5790 $instructions
5791 );
5792 // Provide a helpful description with sample instructions button
5793 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>';
5794 echo '<div class="mxchat-instructions-container">';
5795 echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
5796 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">';
5797 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5798 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5799 echo '</svg>';
5800 echo esc_html__('View Sample Instructions', 'mxchat');
5801 echo '</button>';
5802 echo '</div>';
5803
5804 // Add modal to WordPress admin footer instead of inline
5805 add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
5806 }
5807
5808 // New method to render modal in admin footer
5809 public function render_sample_instructions_modal() {
5810 static $modal_rendered = false;
5811 if ($modal_rendered) return; // Prevent duplicate modals
5812 $modal_rendered = true;
5813
5814 echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
5815 echo '<div class="mxchat-instructions-modal-content">';
5816 echo '<div class="mxchat-instructions-modal-header">';
5817 echo '<h3 class="mxchat-instructions-modal-title">';
5818 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">';
5819 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
5820 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
5821 echo '</svg>';
5822 echo esc_html__('Sample AI Instructions', 'mxchat');
5823 echo '</h3>';
5824 echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
5825 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">';
5826 echo '<line x1="18" y1="6" x2="6" y2="18"/>';
5827 echo '<line x1="6" y1="6" x2="18" y2="18"/>';
5828 echo '</svg>';
5829 echo '</button>';
5830 echo '</div>';
5831 echo '<div class="mxchat-instructions-modal-body">';
5832 echo '<div class="mxchat-instructions-content">';
5833 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:
5834
5835 # Response Style - CRITICALLY IMPORTANT
5836 - MAXIMUM LENGTH: 1-3 short sentences per response
5837 - Ultra-concise: Get straight to the answer with no filler
5838 - No introductions like "Sure!" or "I\'d be happy to help"
5839 - No phrases like "based on my knowledge" or "according to information"
5840 - No explanatory text before giving the answer
5841 - No summaries or repetition
5842 - Hyperlink all URLs
5843 - Respond in user\'s language
5844 - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
5845
5846 # Knowledge Base Requirements - PREVENT HALLUCINATIONS
5847 - ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
5848 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
5849 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
5850 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
5851 - Better to admit insufficient information than provide inaccurate answers');
5852 echo '</div>';
5853 echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
5854 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">';
5855 echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
5856 echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
5857 echo '</svg>';
5858 echo esc_html__('Copy Instructions', 'mxchat');
5859 echo '</button>';
5860 echo '</div>';
5861 echo '<div class="mxchat-instructions-modal-footer">';
5862 echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
5863 echo '</div>';
5864 echo '</div>';
5865 echo '</div>';
5866 }
5867
5868
5869 public function mxchat_model_callback() {
5870 // Define available models grouped by provider
5871 $models = array(
5872 esc_html__('OpenRouter (100+ Models)', 'mxchat') => array(
5873 'openrouter' => esc_html__('OpenRouter - Select after entering API key', 'mxchat'),
5874 ),
5875 esc_html__('Google Gemini Models', 'mxchat') => array(
5876 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5877 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5878 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5879 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5880 ),
5881 esc_html__('Google Gemini Models', 'mxchat') => array(
5882 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
5883 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
5884 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
5885 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
5886 ),
5887 esc_html__('X.AI Models', 'mxchat') => array(
5888 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
5889 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
5890 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
5891 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
5892 'grok-2' => esc_html__('Grok 2', 'mxchat'),
5893 'grok-4-0709' => esc_html__('Grok 4 (Latest Flagship)', 'mxchat'),
5894 'grok-4-1-fast-reasoning' => esc_html__('Grok 4.1 Fast (Reasoning)', 'mxchat'),
5895 'grok-4-1-fast-non-reasoning' => esc_html__('Grok 4.1 Fast (Non-Reasoning)', 'mxchat'),
5896 ),
5897 esc_html__('DeepSeek Models', 'mxchat') => array(
5898 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
5899 ),
5900 esc_html__('Claude Models', 'mxchat') => array(
5901 'claude-sonnet-4-5-20250929' => esc_html__('Claude Sonnet 4.5 (Best for Agents & Coding)', 'mxchat'),
5902 'claude-opus-4-1-20250805' => esc_html__('Claude Opus 4.1 (Exceptional for Complex Tasks)', 'mxchat'),
5903 'claude-haiku-4-5-20251001' => esc_html__('Claude Haiku 4.5 (Fastest & Most Intelligent)', 'mxchat'),
5904 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
5905 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
5906 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
5907 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
5908 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
5909 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat'),
5910 ),
5911 esc_html__('OpenAI Models', 'mxchat') => array(
5912 // GPT-5 family
5913 'gpt-5.1-2025-11-13' => esc_html__('GPT-5.1 (Flagship for Coding & Agentic Tasks)', 'mxchat'),
5914 'gpt-5' => esc_html__('GPT-5 (Flagship for Coding, Reasoning & Agents)', 'mxchat'),
5915 'gpt-5-mini' => esc_html__('GPT-5 Mini (Faster, Cost-Efficient for Precise Prompts)', 'mxchat'),
5916 'gpt-5-nano' => esc_html__('GPT-5 Nano (Fastest & Cheapest for Summarization/Classification)', 'mxchat'),
5917
5918 // Existing OpenAI models
5919 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
5920 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
5921 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
5922 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
5923 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
5924 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat'),
5925 ),
5926 );
5927
5928 // Retrieve the currently selected model from saved options
5929 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
5930
5931 // Begin the select dropdown
5932 echo '<select id="model" name="model">';
5933
5934 // Iterate over groups of models
5935 foreach ($models as $group_label => $group_models) {
5936 echo '<optgroup label="' . esc_attr($group_label) . '">';
5937
5938 foreach ($group_models as $model_value => $model_label) {
5939 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
5940 }
5941
5942 echo '</optgroup>';
5943 }
5944
5945 echo '</select>';
5946
5947 echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
5948
5949 // Add a note for OpenRouter
5950 echo '<p class="description" id="openrouter-model-note" style="display:none; color: #d63638; font-weight: 500;">';
5951 echo '<span class="dashicons dashicons-info" style="font-size: 16px; vertical-align: middle;"></span> ';
5952 echo esc_html__('After entering your OpenRouter API key above, click the button below to load available models.', 'mxchat');
5953 echo '</p>';
5954
5955 // API Key Status Messages (hidden by default, shown by JS based on selected model)
5956 $has_openai_key = !empty($this->options['api_key']);
5957 $has_claude_key = !empty($this->options['claude_api_key']);
5958 $has_xai_key = !empty($this->options['xai_api_key']);
5959 $has_deepseek_key = !empty($this->options['deepseek_api_key']);
5960 $has_gemini_key = !empty($this->options['gemini_api_key']);
5961 $has_openrouter_key = !empty($this->options['openrouter_api_key']);
5962
5963 // OpenAI/GPT models
5964 echo '<p class="mxchat-api-status" data-provider="openai" style="display:none;">';
5965 if ($has_openai_key) {
5966 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
5967 } else {
5968 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5969 }
5970 echo '</p>';
5971
5972 // Claude models
5973 echo '<p class="mxchat-api-status" data-provider="claude" style="display:none;">';
5974 if ($has_claude_key) {
5975 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Anthropic (Claude) detected', 'mxchat') . '</span>';
5976 } else {
5977 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Anthropic (Claude) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5978 }
5979 echo '</p>';
5980
5981 // X.AI models
5982 echo '<p class="mxchat-api-status" data-provider="xai" style="display:none;">';
5983 if ($has_xai_key) {
5984 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for X.AI (Grok) detected', 'mxchat') . '</span>';
5985 } else {
5986 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for X.AI (Grok) detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5987 }
5988 echo '</p>';
5989
5990 // DeepSeek models
5991 echo '<p class="mxchat-api-status" data-provider="deepseek" style="display:none;">';
5992 if ($has_deepseek_key) {
5993 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for DeepSeek detected', 'mxchat') . '</span>';
5994 } else {
5995 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for DeepSeek detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
5996 }
5997 echo '</p>';
5998
5999 // Gemini models
6000 echo '<p class="mxchat-api-status" data-provider="gemini" style="display:none;">';
6001 if ($has_gemini_key) {
6002 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
6003 } else {
6004 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
6005 }
6006 echo '</p>';
6007
6008 // OpenRouter models
6009 echo '<p class="mxchat-api-status" data-provider="openrouter" style="display:none;">';
6010 if ($has_openrouter_key) {
6011 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenRouter detected', 'mxchat') . '</span>';
6012 } else {
6013 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenRouter detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
6014 }
6015 echo '</p>';
6016
6017 // ADD THESE HIDDEN FIELDS RIGHT HERE:
6018 $openrouter_model = isset($this->options['openrouter_selected_model']) ? esc_attr($this->options['openrouter_selected_model']) : '';
6019 $openrouter_model_name = isset($this->options['openrouter_selected_model_name']) ? esc_attr($this->options['openrouter_selected_model_name']) : '';
6020
6021 echo '<input type="hidden" id="openrouter_selected_model" name="openrouter_selected_model" value="' . $openrouter_model . '" />';
6022 echo '<input type="hidden" id="openrouter_selected_model_name" name="openrouter_selected_model_name" value="' . $openrouter_model_name . '" />';
6023 }
6024
6025 // Update your existing callback method
6026 public function enable_streaming_toggle_callback() {
6027 // Get value from options array, default to 'on'
6028 $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
6029 $checked = ($enabled === 'on') ? 'checked' : '';
6030
6031 echo '<label class="toggle-switch">';
6032 echo sprintf(
6033 '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
6034 esc_attr($checked)
6035 );
6036 echo '<span class="slider"></span>';
6037 echo '</label>';
6038 echo '<p class="description">' .
6039 esc_html__('Enable real-time streaming responses for supported models (OpenAI, Claude, DeepSeek, and Grok). When disabled, responses will load all at once.', 'mxchat') .
6040 '</p>';
6041
6042 // Test button with better styling
6043 echo '<div style="margin-top: 15px;">';
6044 echo '<button type="button" id="mxchat-test-streaming-btn" class="button button-secondary">';
6045 echo '<span class="dashicons dashicons-admin-tools" style="vertical-align: middle; margin-right: 5px;"></span>';
6046 echo esc_html__('Test Streaming Compatibility', 'mxchat');
6047 echo '</button>';
6048 echo '<p id="mxchat-test-streaming-result" style="margin-top:10px; font-weight: bold;"></p>';
6049 echo '</div>';
6050 }
6051
6052 // AJAX handler to fetch OpenRouter models
6053 public function fetch_openrouter_models() {
6054 check_ajax_referer('mxchat_fetch_openrouter_models', 'nonce');
6055
6056 $api_key = isset($_POST['api_key']) ? sanitize_text_field($_POST['api_key']) : '';
6057
6058 if (empty($api_key)) {
6059 wp_send_json_error(array('message' => 'API key is required'));
6060 }
6061
6062 $response = wp_remote_get('https://openrouter.ai/api/v1/models', array(
6063 'headers' => array(
6064 'Authorization' => 'Bearer ' . $api_key,
6065 'Content-Type' => 'application/json',
6066 ),
6067 'timeout' => 15,
6068 ));
6069
6070 if (is_wp_error($response)) {
6071 wp_send_json_error(array('message' => $response->get_error_message()));
6072 }
6073
6074 $body = wp_remote_retrieve_body($response);
6075 $data = json_decode($body, true);
6076
6077 if (isset($data['data']) && is_array($data['data'])) {
6078 // Format the models for the frontend
6079 $models = array_map(function($model) {
6080 return array(
6081 'id' => $model['id'],
6082 'name' => $model['name'] ?? $model['id'],
6083 'description' => $model['description'] ?? '',
6084 'context_length' => $model['context_length'] ?? 0,
6085 'pricing' => array(
6086 'prompt' => $model['pricing']['prompt'] ?? 0,
6087 'completion' => $model['pricing']['completion'] ?? 0,
6088 ),
6089 );
6090 }, $data['data']);
6091
6092 wp_send_json_success(array('models' => $models));
6093 } else {
6094 wp_send_json_error(array('message' => 'Invalid response from OpenRouter'));
6095 }
6096 }
6097
6098 // Callback function for embedding model selection
6099 public function embedding_model_callback() {
6100 $models = array(
6101 esc_html__('OpenAI Embeddings', 'mxchat') => array(
6102 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
6103 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
6104 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
6105 ),
6106 esc_html__('Voyage AI Embeddings', 'mxchat') => array(
6107 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
6108 ),
6109 esc_html__('Google Gemini Embeddings', 'mxchat') => array(
6110 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
6111 )
6112 );
6113 $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
6114 echo '<select id="embedding_model" name="embedding_model">';
6115 foreach ($models as $group_label => $group_models) {
6116 echo '<optgroup label="' . esc_attr($group_label) . '">';
6117 foreach ($group_models as $model_value => $model_label) {
6118 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
6119 }
6120 echo '</optgroup>';
6121 }
6122 echo '</select>';
6123 echo '<p class="description"><span class="red-warning">IMPORTANT:</span> A vector embedding model is required for MxChat to function. Changing models is not recommended; if you do, you must delete all existing knowledge & intent data and reconfigure them.</p>';
6124
6125 // API Key Status Messages for Embedding Models
6126 $has_openai_key = !empty($this->options['api_key']);
6127 $has_voyage_key = !empty($this->options['voyage_api_key']);
6128 $has_gemini_key = !empty($this->options['gemini_api_key']);
6129
6130 // OpenAI Embeddings
6131 echo '<p class="mxchat-embedding-api-status" data-provider="openai" style="display:none;">';
6132 if ($has_openai_key) {
6133 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for OpenAI detected', 'mxchat') . '</span>';
6134 } else {
6135 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for OpenAI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
6136 }
6137 echo '</p>';
6138
6139 // Voyage AI Embeddings
6140 echo '<p class="mxchat-embedding-api-status" data-provider="voyage" style="display:none;">';
6141 if ($has_voyage_key) {
6142 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Voyage AI detected', 'mxchat') . '</span>';
6143 } else {
6144 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Voyage AI detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
6145 }
6146 echo '</p>';
6147
6148 // Gemini Embeddings
6149 echo '<p class="mxchat-embedding-api-status" data-provider="gemini" style="display:none;">';
6150 if ($has_gemini_key) {
6151 echo '<span style="color: #00a32a;">✓ ' . esc_html__('API key for Google Gemini detected', 'mxchat') . '</span>';
6152 } else {
6153 echo '<span style="color: #d63638;">⚠ ' . esc_html__('No API key for Google Gemini detected. Please enter API key in API Keys tab.', 'mxchat') . '</span>';
6154 }
6155 echo '</p>';
6156 }
6157
6158
6159 public function mxchat_top_bar_title_callback() {
6160 // Retrieve the current value of the top bar title from saved options
6161 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
6162
6163 // Render the input field
6164 echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
6165
6166 // Add a description
6167 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
6168 }
6169 public function mxchat_ai_agent_text_callback() {
6170 // Retrieve the current value of the AI agent text from saved options
6171 $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
6172 // Render the input field
6173 echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
6174 // Add a description
6175 echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
6176 }
6177
6178
6179 public function enable_email_block_callback() {
6180 // Load full plugin options array
6181 $all_options = get_option('mxchat_options', []);
6182
6183 // Get the value, default to 'off'
6184 $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
6185
6186 // Check if it's 'on'
6187 $checked = ($enable_email_block === 'on') ? 'checked' : '';
6188
6189 echo '<label class="toggle-switch">';
6190 echo sprintf(
6191 '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
6192 esc_attr($checked)
6193 );
6194 echo '<span class="slider"></span>';
6195 echo '</label>';
6196 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>';
6197 }
6198
6199 public function email_blocker_header_content_callback() {
6200 // Load the entire 'mxchat_options' array
6201 $all_options = get_option('mxchat_options', []);
6202
6203 // Retrieve the saved content or default to empty
6204 $content = isset($all_options['email_blocker_header_content'])
6205 ? $all_options['email_blocker_header_content']
6206 : '';
6207
6208 // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
6209 echo '<textarea
6210 id="email_blocker_header_content"
6211 name="email_blocker_header_content"
6212 rows="5"
6213 cols="70"
6214 data-setting="email_blocker_header_content"
6215 >' . esc_textarea($content) . '</textarea>';
6216
6217 echo '<p class="description">';
6218 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');
6219 echo '</p>';
6220 }
6221
6222 public function email_blocker_button_text_callback() {
6223 // Load the entire 'mxchat_options' array
6224 $all_options = get_option('mxchat_options', []);
6225
6226 // Retrieve the saved button text or default to empty
6227 $button_text = isset($all_options['email_blocker_button_text'])
6228 ? $all_options['email_blocker_button_text']
6229 : '';
6230
6231 // Use esc_attr to safely render the existing text
6232 echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
6233
6234 echo '<p class="description">';
6235 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
6236 echo '</p>';
6237 }
6238
6239 //Enable name field callback
6240 public function enable_name_field_callback() {
6241 // Load full plugin options array
6242 $all_options = get_option('mxchat_options', []);
6243 // Get the value, default to 'off'
6244 $enable_name_field = isset($all_options['enable_name_field']) ? $all_options['enable_name_field'] : 'off';
6245 // Check if it's 'on'
6246 $checked = ($enable_name_field === 'on') ? 'checked' : '';
6247 echo '<label class="toggle-switch">';
6248 echo sprintf(
6249 '<input type="checkbox" id="enable_name_field" name="enable_name_field" value="on" %s />',
6250 esc_attr($checked)
6251 );
6252 echo '<span class="slider"></span>';
6253 echo '</label>';
6254 echo '<p class="description">' . esc_html__('Enable this to also require users to enter their name along with their email before chatting.', 'mxchat') . '</p>';
6255 }
6256 //Name field placeholder callback
6257 public function name_field_placeholder_callback() {
6258 $all_options = get_option('mxchat_options', []);
6259 $placeholder = isset($all_options['name_field_placeholder'])
6260 ? $all_options['name_field_placeholder']
6261 : esc_html__('Enter your name', 'mxchat');
6262
6263 echo '<input type="text" id="name_field_placeholder" name="name_field_placeholder" value="' . esc_attr($placeholder) . '" style="width: 300px;" />';
6264 echo '<p class="description">';
6265 echo esc_html__('Placeholder text for the name field.', 'mxchat');
6266 echo '</p>';
6267 }
6268
6269
6270
6271 public function mxchat_intro_message_callback() {
6272 // Load the entire 'mxchat_options' array
6273 $all_options = get_option('mxchat_options', []);
6274 // Retrieve the saved intro message or use the default
6275 $default_message = __('Hello! How can I assist you today?', 'mxchat');
6276 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
6277 // Output the textarea with the saved value without escaping HTML
6278 ?>
6279 <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
6280 <p class="description">
6281 <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
6282 </p>
6283 <?php
6284 }
6285
6286 public function mxchat_input_copy_callback() {
6287 // Load the entire 'mxchat_options' array
6288 $all_options = get_option('mxchat_options', []);
6289
6290 // Retrieve the saved input copy or use the default value
6291 $default_copy = __('How can I assist?', 'mxchat');
6292 $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
6293
6294 // Output the input field with the saved value
6295 printf(
6296 '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
6297 esc_attr($input_copy),
6298 esc_attr__('How can I assist?', 'mxchat')
6299 );
6300
6301 // Output the description
6302 echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
6303 }
6304
6305
6306
6307 public function mxchat_user_message_font_color_callback() {
6308 $disabled = $this->is_activated ? '' : 'disabled';
6309 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6310
6311 echo '<div class="' . esc_attr($class) . '">';
6312 echo sprintf(
6313 '<input type="text"
6314 id="user_message_font_color"
6315 name="user_message_font_color"
6316 value="%s"
6317 class="my-color-field"
6318 data-default-color="#ffffff"
6319 %s />',
6320 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
6321 esc_attr($disabled)
6322 );
6323
6324 if (!$this->is_activated) {
6325 echo '<div class="pro-feature-overlay">';
6326 echo '<a href="https://mxchat.ai/" target="_blank">';
6327 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6328 echo '</a>';
6329 echo '</div>';
6330 }
6331 echo '</div>';
6332 }
6333
6334 public function mxchat_bot_message_bg_color_callback() {
6335 $disabled = $this->is_activated ? '' : 'disabled';
6336 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6337
6338 echo '<div class="' . esc_attr($class) . '">';
6339 echo sprintf(
6340 '<input type="text"
6341 id="bot_message_bg_color"
6342 name="bot_message_bg_color"
6343 value="%s"
6344 class="my-color-field"
6345 data-default-color="#e1e1e1"
6346 %s />',
6347 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
6348 esc_attr($disabled)
6349 );
6350
6351 if (!$this->is_activated) {
6352 echo '<div class="pro-feature-overlay">';
6353 echo '<a href="https://mxchat.ai/" target="_blank">';
6354 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6355 echo '</a>';
6356 echo '</div>';
6357 }
6358 echo '</div>';
6359 }
6360
6361 public function mxchat_bot_message_font_color_callback() {
6362 $disabled = $this->is_activated ? '' : 'disabled';
6363 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6364
6365 echo '<div class="' . esc_attr($class) . '">';
6366 echo sprintf(
6367 '<input type="text"
6368 id="bot_message_font_color"
6369 name="bot_message_font_color"
6370 value="%s"
6371 class="my-color-field"
6372 data-default-color="#333333"
6373 %s />',
6374 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
6375 esc_attr($disabled)
6376 );
6377
6378 if (!$this->is_activated) {
6379 echo '<div class="pro-feature-overlay">';
6380 echo '<a href="https://mxchat.ai/" target="_blank">';
6381 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6382 echo '</a>';
6383 echo '</div>';
6384 }
6385 echo '</div>';
6386 }
6387
6388 public function mxchat_live_agent_message_bg_color_callback() {
6389 $disabled = $this->is_activated ? '' : 'disabled';
6390 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6391
6392 echo '<div class="' . esc_attr($class) . '">';
6393 echo sprintf(
6394 '<input type="text"
6395 id="live_agent_message_bg_color"
6396 name="live_agent_message_bg_color"
6397 value="%s"
6398 class="my-color-field"
6399 data-default-color="#ffffff"
6400 %s />',
6401 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
6402 esc_attr($disabled)
6403 );
6404
6405 if (!$this->is_activated) {
6406 echo '<div class="pro-feature-overlay">';
6407 echo '<a href="https://mxchat.ai/" target="_blank">';
6408 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6409 echo '</a>';
6410 echo '</div>';
6411 }
6412 echo '</div>';
6413 }
6414
6415 public function mxchat_live_agent_message_font_color_callback() {
6416 $disabled = $this->is_activated ? '' : 'disabled';
6417 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6418
6419 echo '<div class="' . esc_attr($class) . '">';
6420 echo sprintf(
6421 '<input type="text"
6422 id="live_agent_message_font_color"
6423 name="live_agent_message_font_color"
6424 value="%s"
6425 class="my-color-field"
6426 data-default-color="#333333"
6427 %s />',
6428 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
6429 esc_attr($disabled)
6430 );
6431
6432 if (!$this->is_activated) {
6433 echo '<div class="pro-feature-overlay">';
6434 echo '<a href="https://mxchat.ai/" target="_blank">';
6435 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6436 echo '</a>';
6437 echo '</div>';
6438 }
6439 echo '</div>';
6440 }
6441
6442 public function mxchat_mode_indicator_bg_color_callback() {
6443 $disabled = $this->is_activated ? '' : 'disabled';
6444 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6445
6446 echo '<div class="' . esc_attr($class) . '">';
6447 echo sprintf(
6448 '<input type="text"
6449 id="mode_indicator_bg_color"
6450 name="mode_indicator_bg_color"
6451 value="%s"
6452 class="my-color-field"
6453 data-default-color="#767676"
6454 %s />',
6455 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
6456 esc_attr($disabled)
6457 );
6458
6459 if (!$this->is_activated) {
6460 echo '<div class="pro-feature-overlay">';
6461 echo '<a href="https://mxchat.ai/" target="_blank">';
6462 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6463 echo '</a>';
6464 echo '</div>';
6465 }
6466 echo '</div>';
6467 }
6468
6469 public function mxchat_mode_indicator_font_color_callback() {
6470 $disabled = $this->is_activated ? '' : 'disabled';
6471 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6472
6473 echo '<div class="' . esc_attr($class) . '">';
6474 echo sprintf(
6475 '<input type="text"
6476 id="mode_indicator_font_color"
6477 name="mode_indicator_font_color"
6478 value="%s"
6479 class="my-color-field"
6480 data-default-color="#ffffff"
6481 %s />',
6482 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
6483 esc_attr($disabled)
6484 );
6485
6486 if (!$this->is_activated) {
6487 echo '<div class="pro-feature-overlay">';
6488 echo '<a href="https://mxchat.ai/" target="_blank">';
6489 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6490 echo '</a>';
6491 echo '</div>';
6492 }
6493 echo '</div>';
6494 }
6495
6496 public function mxchat_toolbar_icon_color_callback() {
6497 $disabled = $this->is_activated ? '' : 'disabled';
6498 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6499
6500 echo '<div class="' . esc_attr($class) . '">';
6501 echo sprintf(
6502 '<input type="text"
6503 id="toolbar_icon_color"
6504 name="toolbar_icon_color"
6505 value="%s"
6506 class="my-color-field"
6507 data-default-color="#212121"
6508 %s />',
6509 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
6510 esc_attr($disabled)
6511 );
6512
6513 if (!$this->is_activated) {
6514 echo '<div class="pro-feature-overlay">';
6515 echo '<a href="https://mxchat.ai/" target="_blank">';
6516 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6517 echo '</a>';
6518 echo '</div>';
6519 }
6520 echo '</div>';
6521 }
6522
6523 public function mxchat_top_bar_bg_color_callback() {
6524 $disabled = $this->is_activated ? '' : 'disabled';
6525 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6526
6527 echo '<div class="' . esc_attr($class) . '">';
6528 echo sprintf(
6529 '<input type="text"
6530 id="top_bar_bg_color"
6531 name="top_bar_bg_color"
6532 value="%s"
6533 class="my-color-field"
6534 data-default-color="#00b294"
6535 %s />',
6536 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
6537 esc_attr($disabled)
6538 );
6539
6540 if (!$this->is_activated) {
6541 echo '<div class="pro-feature-overlay">';
6542 echo '<a href="https://mxchat.ai/" target="_blank">';
6543 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6544 echo '</a>';
6545 echo '</div>';
6546 }
6547 echo '</div>';
6548 }
6549
6550 public function mxchat_send_button_font_color_callback() {
6551 $disabled = $this->is_activated ? '' : 'disabled';
6552 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6553
6554 echo '<div class="' . esc_attr($class) . '">';
6555 echo sprintf(
6556 '<input type="text"
6557 id="send_button_font_color"
6558 name="send_button_font_color"
6559 value="%s"
6560 class="my-color-field"
6561 data-default-color="#ffffff"
6562 %s />',
6563 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
6564 esc_attr($disabled)
6565 );
6566
6567 if (!$this->is_activated) {
6568 echo '<div class="pro-feature-overlay">';
6569 echo '<a href="https://mxchat.ai/" target="_blank">';
6570 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6571 echo '</a>';
6572 echo '</div>';
6573 }
6574 echo '</div>';
6575 }
6576
6577 public function mxchat_chatbot_background_color_callback() {
6578 $disabled = $this->is_activated ? '' : 'disabled';
6579 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6580
6581 echo '<div class="' . esc_attr($class) . '">';
6582 echo sprintf(
6583 '<input type="text"
6584 id="chatbot_background_color"
6585 name="chatbot_background_color"
6586 value="%s"
6587 class="my-color-field"
6588 data-default-color="#000000"
6589 %s />',
6590 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
6591 esc_attr($disabled)
6592 );
6593
6594 if (!$this->is_activated) {
6595 echo '<div class="pro-feature-overlay">';
6596 echo '<a href="https://mxchat.ai/" target="_blank">';
6597 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6598 echo '</a>';
6599 echo '</div>';
6600 }
6601 echo '</div>';
6602 }
6603
6604 public function mxchat_icon_color_callback() {
6605 $disabled = $this->is_activated ? '' : 'disabled';
6606 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6607
6608 echo '<div class="' . esc_attr($class) . '">';
6609 echo sprintf(
6610 '<input type="text"
6611 id="icon_color"
6612 name="icon_color"
6613 value="%s"
6614 class="my-color-field"
6615 data-default-color="#ffffff"
6616 %s />',
6617 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
6618 esc_attr($disabled)
6619 );
6620
6621 if (!$this->is_activated) {
6622 echo '<div class="pro-feature-overlay">';
6623 echo '<a href="https://mxchat.ai/" target="_blank">';
6624 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6625 echo '</a>';
6626 echo '</div>';
6627 }
6628 echo '</div>';
6629 }
6630
6631 public function mxchat_custom_icon_callback() {
6632 $disabled = $this->is_activated ? '' : 'disabled';
6633 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6634 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
6635
6636 echo '<div class="' . esc_attr($class) . '">';
6637 echo sprintf(
6638 '<input type="url"
6639 id="custom_icon"
6640 name="custom_icon"
6641 value="%s"
6642 placeholder="%s"
6643 class="regular-text"
6644 %s />',
6645 $custom_icon_url,
6646 esc_attr__('Enter PNG URL', 'mxchat'),
6647 esc_attr($disabled)
6648 );
6649
6650 // Preview container for the icon
6651 if (!empty($custom_icon_url)) {
6652 echo '<div class="icon-preview" style="margin-top: 10px;">';
6653 echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
6654 echo '</div>';
6655 }
6656
6657 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
6658
6659 if (!$this->is_activated) {
6660 echo '<div class="pro-feature-overlay">';
6661 echo '<a href="https://mxchat.ai/" target="_blank">';
6662 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6663 echo '</a>';
6664 echo '</div>';
6665 }
6666 echo '</div>';
6667 }
6668
6669 public function mxchat_title_icon_callback() {
6670 $disabled = $this->is_activated ? '' : 'disabled';
6671 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6672 // Fixed the variable reference - it was using custom_icon instead of title_icon
6673 $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
6674
6675 echo '<div class="' . esc_attr($class) . '">';
6676 echo sprintf(
6677 '<input type="url"
6678 id="title_icon"
6679 name="title_icon"
6680 value="%s"
6681 placeholder="%s"
6682 class="regular-text"
6683 %s />',
6684 $title_icon_url,
6685 esc_attr__('Enter PNG URL', 'mxchat'),
6686 esc_attr($disabled)
6687 );
6688
6689 // Preview container for the icon
6690 if (!empty($title_icon_url)) {
6691 echo '<div class="icon-preview" style="margin-top: 10px;">';
6692 echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
6693 echo '</div>';
6694 }
6695
6696 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
6697
6698 if (!$this->is_activated) {
6699 echo '<div class="pro-feature-overlay">';
6700 echo '<a href="https://mxchat.ai/" target="_blank">';
6701 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6702 echo '</a>';
6703 echo '</div>';
6704 }
6705 echo '</div>';
6706 }
6707
6708 public function mxchat_chat_input_font_color_callback() {
6709 $disabled = $this->is_activated ? '' : 'disabled';
6710 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
6711
6712 echo '<div class="' . esc_attr($class) . '">';
6713 echo sprintf(
6714 '<input type="text"
6715 id="chat_input_font_color"
6716 name="chat_input_font_color"
6717 value="%s"
6718 class="my-color-field"
6719 data-default-color="#555555"
6720 %s />',
6721 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
6722 esc_attr($disabled)
6723 );
6724
6725 if (!$this->is_activated) {
6726 echo '<div class="pro-feature-overlay">';
6727 echo '<a href="https://mxchat.ai/" target="_blank">';
6728 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
6729 echo '</a>';
6730 echo '</div>';
6731 }
6732 echo '</div>';
6733 }
6734
6735 public function mxchat_append_to_body_callback() {
6736 // Get value from options array, default to 'off'
6737 $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
6738 $checked = ($append_to_body === 'on') ? 'checked' : '';
6739
6740 echo '<label class="toggle-switch">';
6741 echo sprintf(
6742 '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
6743 esc_attr($checked)
6744 );
6745 echo '<span class="slider"></span>';
6746 echo '</label>';
6747 echo '<p class="description">' .
6748 esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
6749 '</p>';
6750
6751 }
6752
6753 public function mxchat_contextual_awareness_callback() {
6754 // Get value from options array, default to 'off'
6755 $contextual_awareness = isset($this->options['contextual_awareness_toggle']) ? $this->options['contextual_awareness_toggle'] : 'off';
6756 $checked = ($contextual_awareness === 'on') ? 'checked' : '';
6757 echo '<label class="toggle-switch">';
6758 echo sprintf(
6759 '<input type="checkbox" id="contextual_awareness_toggle" name="contextual_awareness_toggle" value="on" %s />',
6760 esc_attr($checked)
6761 );
6762 echo '<span class="slider"></span>';
6763 echo '</label>';
6764 echo '<p class="description">' .
6765 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') .
6766 '</p>';
6767 }
6768
6769
6770 public function mxchat_privacy_toggle_callback() {
6771 // Load from mxchat_options array
6772 $options = get_option('mxchat_options', []);
6773
6774 // Get privacy toggle value with fallback
6775 $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
6776 $checked = ($privacy_toggle === 'on') ? 'checked' : '';
6777
6778 // Get privacy text with fallback
6779 $privacy_text = isset($options['privacy_text'])
6780 ? $options['privacy_text']
6781 : __('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat');
6782
6783 // Output the toggle switch
6784 echo '<label class="toggle-switch">';
6785 echo sprintf(
6786 '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
6787 esc_attr($checked)
6788 );
6789 echo '<span class="slider"></span>';
6790 echo '</label>';
6791 echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
6792
6793 // Output the custom text input field
6794 echo sprintf(
6795 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
6796 esc_textarea($privacy_text)
6797 );
6798 echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
6799 }
6800
6801
6802 public function mxchat_complianz_toggle_callback() {
6803 // Load from mxchat_options array
6804 $options = get_option('mxchat_options', []);
6805
6806 // Get complianz toggle value with fallback
6807 $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
6808 $checked = ($complianz_toggle === 'on') ? 'checked' : '';
6809
6810 // Output the toggle switch
6811 echo '<label class="toggle-switch">';
6812 echo sprintf(
6813 '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
6814 esc_attr($checked)
6815 );
6816 echo '<span class="slider"></span>';
6817 echo '</label>';
6818
6819 echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
6820 }
6821
6822 public function mxchat_link_target_toggle_callback() {
6823 // Load from mxchat_options array
6824 $options = get_option('mxchat_options', []);
6825
6826 // Get link target toggle value with fallback
6827 $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
6828 $checked = ($link_target_toggle === 'on') ? 'checked' : '';
6829
6830 // Output the toggle switch
6831 echo '<label class="toggle-switch">';
6832 echo sprintf(
6833 '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
6834 esc_attr($checked)
6835 );
6836 echo '<span class="slider"></span>';
6837 echo '</label>';
6838 echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
6839 }
6840
6841 public function mxchat_chat_persistence_toggle_callback() {
6842 // Load from mxchat_options array
6843 $options = get_option('mxchat_options', []);
6844
6845 // Get chat persistence toggle value with fallback
6846 $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
6847 $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
6848
6849 // Output the toggle switch
6850 echo '<label class="toggle-switch">';
6851 echo sprintf(
6852 '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
6853 esc_attr($checked)
6854 );
6855 echo '<span class="slider"></span>';
6856 echo '</label>';
6857
6858 echo '<p class="description">' . esc_html__('Enable to keep chat history when users navigate tabs or return to the site within 24 hours.', 'mxchat') . '</p>';
6859 }
6860
6861 public function mxchat_popular_question_1_callback() {
6862 // Load the full plugin options array
6863 $all_options = get_option('mxchat_options', []);
6864
6865 // Retrieve the specific option for popular_question_1
6866 $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
6867
6868 // Render the input field
6869 printf(
6870 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
6871 esc_attr($popular_question_1),
6872 esc_attr__('Enter Quick Question 1', 'mxchat')
6873 );
6874
6875 // Add a description for the field
6876 echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
6877 }
6878
6879
6880 public function mxchat_popular_question_2_callback() {
6881 // Load the full plugin options array
6882 $all_options = get_option('mxchat_options', []);
6883
6884 // Retrieve the specific option for popular_question_2
6885 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
6886
6887 // Render the input field
6888 printf(
6889 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
6890 esc_attr($popular_question_2),
6891 esc_attr__('Enter Quick Question 2', 'mxchat')
6892 );
6893
6894 // Add a description for the field
6895 echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
6896 }
6897
6898
6899 public function mxchat_popular_question_3_callback() {
6900 // Load the full plugin options array
6901 $all_options = get_option('mxchat_options', []);
6902
6903 // Retrieve the specific option for popular_question_3
6904 $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
6905
6906 // Render the input field
6907 printf(
6908 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
6909 esc_attr($popular_question_3),
6910 esc_attr(__('Enter Quick Question 3', 'mxchat'))
6911 );
6912
6913 // Add a description for the field
6914 echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
6915 }
6916
6917 public function mxchat_additional_popular_questions_callback() {
6918 $options = get_option('mxchat_options', []);
6919 $additional_questions = isset($options['additional_popular_questions'])
6920 ? $options['additional_popular_questions']
6921 : get_option('additional_popular_questions', array());
6922
6923 echo '<div id="mxchat-additional-questions-container">';
6924 if (!empty($additional_questions)) {
6925 foreach ($additional_questions as $index => $question) {
6926 printf(
6927 '<div class="mxchat-question-row">
6928 <input type="text" name="additional_popular_questions[]"
6929 value="%s"
6930 placeholder="%s"
6931 class="regular-text mxchat-question-input"
6932 data-question-index="%d" />
6933 <button type="button" class="button mxchat-remove-question"
6934 aria-label="%s">%s</button>
6935 </div>',
6936 esc_attr($question),
6937 esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
6938 $index,
6939 esc_attr(__('Remove question', 'mxchat')),
6940 esc_html__('Remove', 'mxchat')
6941 );
6942 }
6943 } else {
6944 printf(
6945 '<div class="mxchat-question-row">
6946 <input type="text" name="additional_popular_questions[]"
6947 value=""
6948 placeholder="%s"
6949 class="regular-text mxchat-question-input"
6950 data-question-index="0" />
6951 <button type="button" class="button mxchat-remove-question"
6952 aria-label="%s">%s</button>
6953 </div>',
6954 esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
6955 esc_attr(__('Remove question', 'mxchat')),
6956 esc_html__('Remove', 'mxchat')
6957 );
6958 }
6959 echo '</div>';
6960 printf(
6961 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
6962 esc_attr(__('Add question', 'mxchat')),
6963 esc_html__('Add Question', 'mxchat')
6964 );
6965 echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
6966 echo '</div>';
6967 }
6968
6969 public function mxchat_brave_api_key_callback() {
6970 $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
6971
6972 echo '<div class="api-key-wrapper">';
6973 echo sprintf(
6974 '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
6975 $brave_api_key
6976 );
6977 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
6978 echo '</div>';
6979 echo '<p class="description">' . __('Required for Brave Search integration. Get your API key from Brave Search API.', 'mxchat') . '</p>';
6980 }
6981
6982 public function mxchat_brave_image_count_callback() {
6983 $brave_image_count = isset($this->options['brave_image_count'])
6984 ? intval($this->options['brave_image_count'])
6985 : 4;
6986
6987 echo sprintf(
6988 '<input type="number" id="brave_image_count" name="brave_image_count"
6989 value="%d" min="1" max="6" class="small-text" />',
6990 $brave_image_count
6991 );
6992 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
6993 }
6994
6995 public function mxchat_brave_safe_search_callback() {
6996 $brave_safe_search = isset($this->options['brave_safe_search'])
6997 ? esc_attr($this->options['brave_safe_search'])
6998 : 'strict';
6999
7000 echo '<select id="brave_safe_search" name="brave_safe_search">';
7001 echo sprintf(
7002 '<option value="strict" %s>%s</option>',
7003 selected($brave_safe_search, 'strict', false),
7004 __('Strict', 'mxchat')
7005 );
7006 echo sprintf(
7007 '<option value="off" %s>%s</option>',
7008 selected($brave_safe_search, 'off', false),
7009 __('Off', 'mxchat')
7010 );
7011 echo '</select>';
7012 echo '<p class="description">' .
7013 esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
7014 '</p>';
7015 }
7016
7017 public function mxchat_brave_news_count_callback() {
7018 $brave_news_count = isset($this->options['brave_news_count'])
7019 ? intval($this->options['brave_news_count'])
7020 : 3;
7021
7022 echo sprintf(
7023 '<input type="number" id="brave_news_count" name="brave_news_count"
7024 value="%d" min="1" max="10" class="small-text" />',
7025 $brave_news_count
7026 );
7027 echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
7028 }
7029
7030 public function mxchat_brave_country_callback() {
7031 $brave_country = isset($this->options['brave_country'])
7032 ? esc_attr($this->options['brave_country'])
7033 : 'us';
7034
7035 echo sprintf(
7036 '<input type="text" id="brave_country" name="brave_country"
7037 value="%s" maxlength="2" class="small-text" />',
7038 $brave_country
7039 );
7040 echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
7041 }
7042
7043 public function mxchat_brave_language_callback() {
7044 $brave_language = isset($this->options['brave_language'])
7045 ? esc_attr($this->options['brave_language'])
7046 : 'en';
7047
7048 echo sprintf(
7049 '<input type="text" id="brave_language" name="brave_language"
7050 value="%s" maxlength="2" class="small-text" />',
7051 $brave_language
7052 );
7053 echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
7054 }
7055
7056
7057
7058
7059
7060 // Section Callback
7061 public function mxchat_pdf_intent_section_callback() {
7062 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
7063 }
7064
7065 public function mxchat_chat_toolbar_toggle_callback() {
7066 // Get chat toolbar toggle value with fallback
7067 $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
7068 $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
7069
7070 // Output the toggle switch
7071 echo '<label class="toggle-switch">';
7072 echo sprintf(
7073 '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
7074 esc_attr($checked)
7075 );
7076 echo '<span class="slider"></span>';
7077 echo '</label>';
7078
7079 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>';
7080 }
7081
7082 /**
7083 * Callback for PDF upload button toggle setting
7084 */
7085 public function mxchat_show_pdf_upload_button_callback() {
7086 // Get toggle value with fallback
7087 $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
7088 $checked = ($show_pdf_button === 'on') ? 'checked' : '';
7089
7090 // Output the toggle switch
7091 echo '<label class="toggle-switch">';
7092 echo sprintf(
7093 '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
7094 esc_attr($checked)
7095 );
7096 echo '<span class="slider"></span>';
7097 echo '</label>';
7098
7099 echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
7100 }
7101
7102 /**
7103 * Callback for Word upload button toggle setting
7104 */
7105 public function mxchat_show_word_upload_button_callback() {
7106 // Get toggle value with fallback
7107 $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
7108 $checked = ($show_word_button === 'on') ? 'checked' : '';
7109
7110 // Output the toggle switch
7111 echo '<label class="toggle-switch">';
7112 echo sprintf(
7113 '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
7114 esc_attr($checked)
7115 );
7116 echo '<span class="slider"></span>';
7117 echo '</label>';
7118
7119 echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
7120 }
7121
7122 public function mxchat_pdf_intent_trigger_text_callback() {
7123 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
7124
7125 echo sprintf(
7126 '<textarea id="pdf_intent_trigger_text"
7127 name="pdf_intent_trigger_text"
7128 rows="3"
7129 cols="50"
7130 placeholder="%s">%s</textarea>',
7131 esc_attr__('Enter trigger text', 'mxchat'),
7132 isset($this->options['pdf_intent_trigger_text'])
7133 ? esc_textarea($this->options['pdf_intent_trigger_text'])
7134 : esc_textarea($default_text)
7135 );
7136 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
7137 }
7138
7139 public function mxchat_pdf_intent_success_text_callback() {
7140 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
7141
7142 echo sprintf(
7143 '<textarea id="pdf_intent_success_text"
7144 name="pdf_intent_success_text"
7145 rows="3"
7146 cols="50"
7147 placeholder="%s">%s</textarea>',
7148 esc_attr__('Enter success text', 'mxchat'),
7149 isset($this->options['pdf_intent_success_text'])
7150 ? esc_textarea($this->options['pdf_intent_success_text'])
7151 : esc_textarea($default_text)
7152 );
7153 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
7154 }
7155
7156 public function mxchat_pdf_intent_error_text_callback() {
7157 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
7158
7159 echo sprintf(
7160 '<textarea id="pdf_intent_error_text"
7161 name="pdf_intent_error_text"
7162 rows="3"
7163 cols="50"
7164 placeholder="%s">%s</textarea>',
7165 esc_attr__('Enter error text', 'mxchat'),
7166 isset($this->options['pdf_intent_error_text'])
7167 ? esc_textarea($this->options['pdf_intent_error_text'])
7168 : esc_textarea($default_text)
7169 );
7170 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
7171 }
7172
7173 public function mxchat_pdf_max_pages_callback() {
7174 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
7175
7176 echo sprintf(
7177 '<input type="range"
7178 id="pdf_max_pages"
7179 name="pdf_max_pages"
7180 min="1"
7181 max="69"
7182 value="%d"
7183 class="range-slider" />',
7184 esc_attr($max_pages)
7185 );
7186 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
7187 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
7188 }
7189
7190 public function mxchat_live_agent_status_callback() {
7191 // Always get fresh options instead of using cached $this->options
7192 $fresh_options = get_option('mxchat_options');
7193 $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
7194
7195 echo '<label class="toggle-switch">';
7196 echo sprintf(
7197 '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
7198 checked($status, 'on', false)
7199 );
7200 echo '<span class="slider"></span>';
7201 echo '</label>';
7202 echo '<label for="live_agent_status" class="mxchat-status-label">';
7203 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
7204 echo '</label>';
7205 }
7206
7207 public function mxchat_live_agent_away_message_callback() {
7208 $message = isset($this->options['live_agent_away_message'])
7209 ? $this->options['live_agent_away_message']
7210 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
7211
7212 printf(
7213 '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
7214 esc_textarea($message)
7215 );
7216 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
7217 }
7218
7219 public function mxchat_live_agent_notification_message_callback() {
7220 $message = isset($this->options['live_agent_notification_message'])
7221 ? $this->options['live_agent_notification_message']
7222 : __('Live agent has been notified.', 'mxchat');
7223
7224 printf(
7225 '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
7226 esc_textarea($message)
7227 );
7228 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
7229 }
7230
7231 public function mxchat_live_agent_webhook_url_callback() {
7232 $webhook_url = isset($this->options['live_agent_webhook_url'])
7233 ? esc_url($this->options['live_agent_webhook_url'])
7234 : esc_url(get_option('live_agent_webhook_url', ''));
7235
7236 printf(
7237 '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
7238 $webhook_url
7239 );
7240 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
7241 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
7242 }
7243
7244 public function mxchat_live_agent_secret_key_callback() {
7245 printf(
7246 '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
7247 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
7248 );
7249 echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
7250 echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
7251 }
7252
7253 public function mxchat_live_agent_bot_token_callback() {
7254 printf(
7255 '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
7256 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
7257 );
7258 echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
7259 echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
7260 }
7261
7262 public function mxchat_live_agent_user_ids_callback() {
7263 $user_ids = isset($this->options['live_agent_user_ids'])
7264 ? esc_textarea($this->options['live_agent_user_ids'])
7265 : '';
7266
7267 printf(
7268 '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
7269 $user_ids
7270 );
7271 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>';
7272 }
7273
7274 public function mxchat_similarity_threshold_callback() {
7275 // Load from mxchat_options array
7276 $options = get_option('mxchat_options', []);
7277
7278 // Get value from options array with default of 80
7279 $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
7280
7281 echo '<div class="slider-container">';
7282 echo sprintf(
7283 '<input type="range"
7284 id="similarity_threshold"
7285 name="similarity_threshold"
7286 min="20"
7287 max="85"
7288 step="1"
7289 value="%s"
7290 class="range-slider" />',
7291 esc_attr($threshold)
7292 );
7293 echo sprintf(
7294 '<span id="threshold_value" class="range-value">%s</span>',
7295 esc_html($threshold)
7296 );
7297 echo '</div>';
7298 echo '<p class="description">';
7299 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');
7300 echo '</p>';
7301 }
7302
7303 public function mxchat_enqueue_admin_assets() {
7304 // Get plugin version
7305 $version = MXCHAT_VERSION;
7306
7307 // Use file modification time for development (remove in production)
7308 if (defined('WP_DEBUG') && WP_DEBUG) {
7309 $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
7310 }
7311
7312 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
7313 $plugin_url = plugin_dir_url(__FILE__) . '../';
7314
7315 // Only load on MxChat pages
7316 if (strpos($current_page, 'mxchat') === false) {
7317 return;
7318 }
7319
7320 // Always load these on all MxChat pages
7321 $this->enqueue_core_admin_assets($plugin_url, $version);
7322 $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
7323 $this->localize_admin_scripts($current_page);
7324 }
7325 private function enqueue_core_admin_assets($plugin_url, $version) {
7326 // Core admin styles
7327 wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
7328 wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
7329
7330 // Core admin scripts
7331 wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
7332 }
7333 private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
7334 switch ($current_page) {
7335 case 'mxchat-prompts':
7336 // Knowledge processing page assets
7337 wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
7338 wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
7339 // Add the knowledge processing script
7340 wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
7341 break;
7342
7343 case 'mxchat-transcripts':
7344 wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
7345 wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
7346 break;
7347
7348 case 'mxchat-actions':
7349 wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
7350 break;
7351
7352 case 'mxchat-activation':
7353 wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
7354 break;
7355 default:
7356 wp_enqueue_script(
7357 'mxchat-test-streaming-js',
7358 $plugin_url . 'js/mxchat-test-streaming.js',
7359 ['jquery'],
7360 $version,
7361 true
7362 );
7363
7364 wp_localize_script('mxchat-test-streaming-js', 'mxchatTestStreamingAjax', [
7365 'ajax_url' => admin_url('admin-ajax.php'),
7366 'nonce' => wp_create_nonce('mxchat_test_streaming_nonce')
7367 ]);
7368 break;
7369 }
7370 }
7371 private function localize_admin_scripts($current_page) {
7372 // Base localization data for main admin script
7373 $base_data = array(
7374 'ajax_url' => admin_url('admin-ajax.php'),
7375 'nonce' => wp_create_nonce('mxchat_status_nonce'),
7376 'admin_url' => admin_url(),
7377 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
7378 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
7379 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7380 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
7381 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7382 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
7383 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
7384 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
7385 'fetch_openrouter_models_nonce' => wp_create_nonce('mxchat_fetch_openrouter_models'), // ADD THIS LINE
7386 'is_activated' => $this->is_activated ? '1' : '0',
7387 'status_refresh_interval' => 5000,
7388 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7389 'ajaxurl' => admin_url('admin-ajax.php')
7390 );
7391
7392 // Localize main admin script with base data
7393 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
7394
7395 // Page-specific localizations
7396 $this->localize_page_specific_scripts($current_page);
7397 }
7398 private function localize_page_specific_scripts($current_page) {
7399 switch ($current_page) {
7400 case 'mxchat-prompts':
7401 // Status updater localization
7402 wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
7403 'ajax_url' => admin_url('admin-ajax.php'),
7404 'nonce' => wp_create_nonce('mxchat_status_nonce')
7405 ));
7406
7407 // Content selector localization
7408 wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
7409 'ajaxurl' => admin_url('admin-ajax.php'),
7410 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
7411 'i18n' => array(
7412 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
7413 'selectAll' => __('Select All', 'mxchat'),
7414 'process' => __('Process Selected', 'mxchat'),
7415 'cancel' => __('Cancel', 'mxchat'),
7416 'noResults' => __('No content found.', 'mxchat')
7417 )
7418 ));
7419
7420 wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
7421 'ajax_url' => admin_url('admin-ajax.php'),
7422 'status_nonce' => wp_create_nonce('mxchat_status_nonce'),
7423 'queue_nonce' => wp_create_nonce('mxchat_queue_nonce'),
7424 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
7425 'settings_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7426 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
7427 'admin_url' => admin_url(),
7428 'ajaxurl' => admin_url('admin-ajax.php'),
7429 'status_refresh_interval' => 2000
7430 ));
7431 break;
7432
7433 case 'mxchat-activation':
7434 wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
7435 'ajax_url' => admin_url('admin-ajax.php'),
7436 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
7437 ));
7438 break;
7439
7440 case 'mxchat-settings':
7441 default:
7442 // Color picker and settings localization
7443 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
7444 'ajax_url' => admin_url('admin-ajax.php'),
7445 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
7446 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
7447 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
7448 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
7449 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
7450 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
7451 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
7452 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
7453 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
7454 'icon_color' => $this->options['icon_color'] ?? '#fff',
7455 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
7456 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
7457 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
7458 'loops_api_key' => $this->options['loops_api_key'] ?? '',
7459 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
7460 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
7461 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
7462 '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'),
7463 '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'),
7464 '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'),
7465 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
7466 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
7467 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
7468 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
7469 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
7470 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
7471 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
7472 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
7473 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
7474 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
7475 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
7476 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
7477 ));
7478 break;
7479 }
7480
7481 // Additional localization that was in the original code
7482 wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
7483 'ajax_url' => admin_url('admin-ajax.php'),
7484 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
7485 ));
7486 }
7487
7488 public function mxchat_sanitize($input) {
7489 $new_input = array();
7490
7491 if (isset($input['api_key'])) {
7492 $new_input['api_key'] = sanitize_text_field($input['api_key']);
7493 }
7494
7495 if (isset($input['similarity_threshold'])) {
7496 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
7497 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
7498 }
7499
7500 if (isset($input['xai_api_key'])) {
7501 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
7502 }
7503
7504 if (isset($input['claude_api_key'])) {
7505 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
7506 }
7507
7508 if (isset($input['enable_streaming_toggle'])) {
7509 $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
7510 } else {
7511 // If checkbox not checked, it won't be in $input, so set to 'off'
7512 $new_input['enable_streaming_toggle'] = 'off';
7513 }
7514
7515
7516 if (isset($input['deepseek_api_key'])) {
7517 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
7518 }
7519
7520 if (isset($input['gemini_api_key'])) {
7521 $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
7522 }
7523
7524 if (isset($input['enable_woocommerce_integration'])) {
7525 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
7526 }
7527
7528 if (isset($input['privacy_toggle'])) {
7529 $new_input['privacy_toggle'] = $input['privacy_toggle'];
7530 }
7531
7532 if (isset($input['complianz_toggle'])) {
7533 $new_input['complianz_toggle'] = $input['complianz_toggle'];
7534 }
7535
7536 // Handle custom privacy text input
7537 if (isset($input['privacy_text'])) {
7538 // Allow basic HTML for links
7539 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
7540 }
7541
7542 if (isset($input['system_prompt_instructions'])) {
7543 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
7544 }
7545
7546 if (isset($input['mxchat_pro_email'])) {
7547 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
7548 }
7549
7550 if (isset($input['mxchat_activation_key'])) {
7551 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
7552 }
7553
7554 if (isset($input['append_to_body'])) {
7555 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
7556 }
7557
7558 if (isset($input['contextual_awareness_toggle'])) {
7559 $new_input['contextual_awareness_toggle'] = $input['contextual_awareness_toggle'] === 'on' ? 'on' : 'off';
7560 }
7561
7562 if (isset($input['top_bar_title'])) {
7563 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
7564 }
7565
7566 if (isset($input['ai_agent_text'])) {
7567 $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
7568 }
7569
7570 if (isset($input['enable_email_block'])) {
7571 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
7572 }
7573
7574 if (isset($input['email_blocker_header_content'])) {
7575 // wp_kses_post() allows standard HTML tags permitted by WordPress
7576 $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
7577 }
7578 if (isset($input['email_blocker_button_text'])) {
7579 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
7580 }
7581 // Sanitize name field toggle
7582 if (isset($input['enable_name_field'])) {
7583 $new_input['enable_name_field'] = ($input['enable_name_field'] === 'on') ? 'on' : 'off';
7584 } else {
7585 $new_input['enable_name_field'] = 'off';
7586 }
7587 // Sanitize name field placeholder
7588 if (isset($input['name_field_placeholder'])) {
7589 $new_input['name_field_placeholder'] = sanitize_text_field($input['name_field_placeholder']);
7590 }
7591 if (isset($input['intro_message'])) {
7592 $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
7593 }
7594
7595 if (isset($input['input_copy'])) {
7596 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
7597 }
7598
7599 if (isset($input['rate_limit_message'])) {
7600 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
7601 }
7602
7603 // Handle the new rate limits format
7604 if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
7605 $new_input['rate_limits'] = array();
7606 $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
7607 $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
7608
7609 foreach ($input['rate_limits'] as $role_id => $settings) {
7610 $new_input['rate_limits'][$role_id] = array();
7611
7612 // Sanitize limit
7613 if (isset($settings['limit'])) {
7614 $limit = sanitize_text_field($settings['limit']);
7615 if (in_array($limit, $allowed_limits, true)) {
7616 $new_input['rate_limits'][$role_id]['limit'] = $limit;
7617 } else {
7618 $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
7619 }
7620 }
7621
7622 // Sanitize timeframe
7623 if (isset($settings['timeframe'])) {
7624 $timeframe = sanitize_text_field($settings['timeframe']);
7625 if (in_array($timeframe, $allowed_timeframes, true)) {
7626 $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
7627 } else {
7628 $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
7629 }
7630 }
7631
7632 // Sanitize message
7633 if (isset($settings['message'])) {
7634 $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
7635 }
7636 }
7637 }
7638
7639 if (isset($input['pre_chat_message'])) {
7640 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
7641 }
7642
7643 if (isset($input['voyage_api_key'])) {
7644 $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
7645 }
7646
7647 // Add to your sanitize function
7648 if (isset($input['embedding_model'])) {
7649 $allowed_models = array(
7650 'text-embedding-ada-002',
7651 'text-embedding-3-small',
7652 'text-embedding-3-large',
7653 'voyage-3-large',
7654 'gemini-embedding-exp-03-07'
7655 );
7656 if (in_array($input['embedding_model'], $allowed_models)) {
7657 $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
7658 }
7659 }
7660
7661 if (isset($input['model'])) {
7662 if ($input['model'] === 'openrouter') {
7663 $new_input['model'] = 'openrouter';
7664 } else {
7665 $allowed_models = array(
7666 'gemini-2.0-flash',
7667 'gemini-2.0-flash-lite',
7668 'gemini-1.5-pro',
7669 'gemini-1.5-flash',
7670 'grok-4-0709',
7671 'grok-4-1-fast-reasoning',
7672 'grok-4-1-fast-non-reasoning',
7673 'grok-3-beta',
7674 'grok-3-fast-beta',
7675 'grok-3-mini-beta',
7676 'grok-3-mini-fast-beta',
7677 'grok-2',
7678 'deepseek-chat',
7679 'claude-sonnet-4-5-20250929',
7680 'claude-opus-4-1-20250805',
7681 'claude-haiku-4-5-20251001',
7682 'claude-opus-4-20250514',
7683 'claude-sonnet-4-20250514',
7684 'claude-3-7-sonnet-20250219',
7685 // REMOVED: 'claude-3-5-sonnet-20241022',
7686 'claude-3-opus-20240229',
7687 'claude-3-sonnet-20240229',
7688 'claude-3-haiku-20240307',
7689 'gpt-5.1-2025-11-13',
7690 'gpt-5',
7691 'gpt-5-mini',
7692 'gpt-5-nano',
7693 'gpt-4.1-2025-04-14',
7694 'gpt-4o',
7695 'gpt-4o-mini',
7696 'gpt-4-turbo',
7697 'gpt-4',
7698 'gpt-3.5-turbo',
7699 );
7700
7701 if (in_array($input['model'], $allowed_models)) {
7702 $new_input['model'] = sanitize_text_field($input['model']);
7703 } else {
7704 // Fallback for any deprecated model
7705 $new_input['model'] = 'claude-3-7-sonnet-20250219';
7706 }
7707 }
7708 }
7709
7710 if (isset($input['openrouter_selected_model'])) {
7711 // Just sanitize it, don't validate against a whitelist
7712 $new_input['openrouter_selected_model'] = sanitize_text_field($input['openrouter_selected_model']);
7713 }
7714
7715 // ADD THIS:
7716 if (isset($input['openrouter_selected_model_name'])) {
7717 $new_input['openrouter_selected_model_name'] = sanitize_text_field($input['openrouter_selected_model_name']);
7718 }
7719
7720 if (isset($input['openrouter_api_key'])) {
7721 $new_input['openrouter_api_key'] = sanitize_text_field($input['openrouter_api_key']);
7722 }
7723
7724
7725 if (isset($input['close_button_color'])) {
7726 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
7727 }
7728
7729 if (isset($input['chatbot_bg_color'])) {
7730 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
7731 }
7732
7733 if (isset($input['woocommerce_consumer_key'])) {
7734 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
7735 }
7736
7737 if (isset($input['woocommerce_consumer_secret'])) {
7738 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
7739 }
7740
7741 if (isset($input['user_message_bg_color'])) {
7742 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
7743 }
7744
7745 if (isset($input['user_message_font_color'])) {
7746 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
7747 }
7748
7749 if (isset($input['bot_message_bg_color'])) {
7750 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
7751 }
7752
7753 if (isset($input['bot_message_font_color'])) {
7754 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
7755 }
7756
7757 if (isset($input['live_agent_message_bg_color'])) {
7758 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
7759 }
7760
7761 if (isset($input['live_agent_message_font_color'])) {
7762 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
7763 }
7764
7765 if (isset($input['mode_indicator_bg_color'])) {
7766 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
7767 }
7768
7769 if (isset($input['mode_indicator_font_color'])) {
7770 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
7771 }
7772
7773 if (isset($input['toolbar_icon_color'])) {
7774 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
7775 }
7776
7777 if (isset($input['top_bar_bg_color'])) {
7778 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
7779 }
7780
7781 if (isset($input['quick_questions_toggle_color'])) {
7782 $new_input['quick_questions_toggle_color'] = sanitize_hex_color($input['quick_questions_toggle_color']);
7783 }
7784
7785 if (isset($input['send_button_font_color'])) {
7786 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
7787 }
7788
7789 if (isset($input['chatbot_background_color'])) {
7790 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
7791 }
7792
7793 if (isset($input['icon_color'])) {
7794 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
7795 }
7796
7797 if (isset($input['custom_icon'])) {
7798 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
7799 }
7800
7801 if (isset($input['title_icon'])) {
7802 $new_input['title_icon'] = esc_url_raw($input['title_icon']);
7803 }
7804
7805 if (isset($input['chat_input_font_color'])) {
7806 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
7807 }
7808
7809 // Sanitize link_target_toggle
7810 if (isset($input['link_target_toggle'])) {
7811 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
7812 }
7813
7814 // Sanitize Loops API Key
7815 if (isset($input['loops_api_key'])) {
7816 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
7817 }
7818
7819 if (isset($input['chat_persistence_toggle'])) {
7820 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
7821 }
7822
7823 if (isset($input['popular_question_1'])) {
7824 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
7825 }
7826
7827 if (isset($input['popular_question_2'])) {
7828 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
7829 }
7830
7831 if (isset($input['popular_question_3'])) {
7832 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
7833 }
7834
7835 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
7836 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
7837 }
7838
7839 // Sanitize Loops Mailing List
7840 if (isset($input['loops_mailing_list'])) {
7841 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
7842 }
7843
7844 // Sanitize Triggered Phrase Response
7845 if (isset($input['triggered_phrase_response'])) {
7846 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
7847 }
7848 if (isset($input['email_capture_response'])) {
7849 $new_input['email_capture_response'] = wp_kses_post($input['email_capture_response']);
7850 }
7851
7852 // Sanitize Brave Search Settings
7853 if (isset($input['brave_api_key'])) {
7854 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
7855 }
7856
7857 if (isset($input['brave_image_count'])) {
7858 $image_count = intval($input['brave_image_count']);
7859 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
7860 }
7861
7862 if (isset($input['brave_safe_search'])) {
7863 $allowed = array('strict', 'off');
7864 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
7865 }
7866
7867 if (isset($input['brave_news_count'])) {
7868 $news_count = intval($input['brave_news_count']);
7869 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
7870 }
7871
7872 if (isset($input['brave_country'])) {
7873 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
7874 }
7875
7876 if (isset($input['brave_language'])) {
7877 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
7878 }
7879
7880 if (isset($input['chat_toolbar_toggle'])) {
7881 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
7882 }
7883
7884 // Sanitize PDF upload button toggle
7885 if (isset($input['show_pdf_upload_button'])) {
7886 $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
7887 } else {
7888 $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
7889 }
7890
7891 // Sanitize Word upload button toggle
7892 if (isset($input['show_word_upload_button'])) {
7893 $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
7894 } else {
7895 $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
7896 }
7897
7898 if (isset($input['pdf_intent_trigger_text'])) {
7899 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
7900 }
7901
7902 if (isset($input['pdf_intent_success_text'])) {
7903 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
7904 }
7905
7906 if (isset($input['pdf_intent_error_text'])) {
7907 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
7908 }
7909
7910 if (isset($input['pdf_max_pages'])) {
7911 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
7912 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
7913 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
7914 }
7915 }
7916
7917 if (isset($input['live_agent_webhook_url'])) {
7918 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
7919 }
7920 if (isset($input['live_agent_secret_key'])) {
7921 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
7922 }
7923
7924 // Live Agent Integration
7925 if (isset($input['live_agent_bot_token'])) {
7926 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
7927 }
7928
7929 if (isset($input['live_agent_user_ids'])) {
7930 $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
7931 }
7932
7933 if (isset($input['live_agent_status'])) {
7934 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
7935 }
7936 if (isset($input['live_agent_away_message'])) {
7937 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
7938 }
7939 if (isset($input['live_agent_notification_message'])) {
7940 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
7941 }
7942
7943 return $new_input;
7944 }
7945
7946
7947 // Method to append the chatbot to the body
7948 public function mxchat_append_chatbot_to_body() {
7949 $options = get_option('mxchat_options');
7950 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
7951 echo do_shortcode('[mxchat_chatbot floating="yes"]');
7952 }
7953 }
7954
7955
7956
7957
7958
7959 private function mxchat_fetch_loops_mailing_lists($api_key) {
7960 $url = 'https://app.loops.so/api/v1/lists';
7961 $response = wp_remote_get($url, array(
7962 'headers' => array(
7963 'Authorization' => 'Bearer ' . $api_key,
7964 'Content-Type' => 'application/json'
7965 )
7966 ));
7967
7968 if (is_wp_error($response)) {
7969 return array();
7970 }
7971
7972 $body = wp_remote_retrieve_body($response);
7973 $lists = json_decode($body, true);
7974
7975 return isset($lists) && is_array($lists) ? $lists : array();
7976 }
7977
7978 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
7979 if (empty($vec1) || empty($vec2)) {
7980 return 0.0;
7981 }
7982
7983 $dot_product = 0.0;
7984 $norm_a = 0.0;
7985 $norm_b = 0.0;
7986
7987 for ($i = 0; $i < count($vec1); $i++) {
7988 $dot_product += $vec1[$i] * $vec2[$i];
7989 $norm_a += pow($vec1[$i], 2);
7990 $norm_b += pow($vec2[$i], 2);
7991 }
7992
7993 if ($norm_a == 0.0 || $norm_b == 0.0) {
7994 return 0.0;
7995 } else {
7996 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
7997 }
7998 }
7999
8000 /**
8001 * Validates nonce and deletes all chat prompts
8002 */
8003 public function mxchat_handle_delete_all_prompts() {
8004 //error_log('=== DELETE ALL DEBUG START ===');
8005
8006 // Verify nonce
8007 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
8008 //error_log('DEBUG: Nonce verification failed');
8009 wp_die(__('Nonce verification failed.', 'mxchat'));
8010 }
8011
8012 // Check permissions
8013 if (!current_user_can('manage_options')) {
8014 //error_log('DEBUG: Permission check failed');
8015 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
8016 }
8017
8018 // Get bot_id from POST data
8019 $bot_id = isset($_POST['bot_id']) ? sanitize_text_field($_POST['bot_id']) : 'default';
8020 //error_log('DEBUG: Bot ID from POST: ' . $bot_id);
8021
8022 $success = true;
8023 $error_messages = array();
8024
8025 // Get bot-specific Pinecone configuration
8026 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
8027 $pinecone_options = $pinecone_manager->mxchat_get_bot_pinecone_options($bot_id);
8028
8029 //error_log('DEBUG: Pinecone options retrieved:');
8030 //error_log(' - Use Pinecone: ' . ($pinecone_options['mxchat_use_pinecone'] ?? 'NOT SET'));
8031 //error_log(' - API Key: ' . (empty($pinecone_options['mxchat_pinecone_api_key']) ? 'EMPTY' : 'SET'));
8032 //error_log(' - Host: ' . ($pinecone_options['mxchat_pinecone_host'] ?? 'NOT SET'));
8033
8034 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
8035
8036 if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
8037 //error_log('[MXCHAT-DELETE] Pinecone is enabled for bot ' . $bot_id . ' - deleting all from Pinecone');
8038
8039 // Delete from bot-specific Pinecone index
8040 $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
8041
8042 //error_log('DEBUG: Delete all result: ' . ($result['success'] ? 'SUCCESS' : 'FAILED'));
8043 if (!$result['success']) {
8044 //error_log('DEBUG: Delete all error: ' . $result['message']);
8045 }
8046
8047 if (!$result['success']) {
8048 $success = false;
8049 $error_messages[] = $result['message'];
8050 }
8051
8052 // No cache clearing needed since we removed caching
8053
8054 } else {
8055 //error_log('[MXCHAT-DELETE] Pinecone not enabled for bot ' . $bot_id . ' - deleting from WordPress database');
8056
8057 // Delete from WordPress database
8058 global $wpdb;
8059 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
8060
8061 // If multi-bot is active and we have a specific bot, delete only that bot's content
8062 if ($bot_id !== 'default' && class_exists('MxChat_Multi_Bot_Manager')) {
8063 $result = $wpdb->delete(
8064 $table_name,
8065 array('bot_id' => $bot_id),
8066 array('%s')
8067 );
8068 } else {
8069 // Delete all content for default bot
8070 $result = $wpdb->query("DELETE FROM {$table_name}");
8071 }
8072
8073 if ($result === false) {
8074 $success = false;
8075 $error_messages[] = 'Failed to delete from WordPress database';
8076 }
8077 }
8078
8079 // No cache clearing needed since we removed caching
8080
8081 //error_log('=== DELETE ALL DEBUG END ===');
8082
8083 // Redirect back with a success message and bot_id
8084 $redirect_url = add_query_arg(array(
8085 'page' => 'mxchat-prompts',
8086 'bot_id' => $bot_id,
8087 'all_deleted' => $success ? 'true' : 'false'
8088 ), admin_url('admin.php'));
8089
8090 wp_safe_redirect($redirect_url);
8091 exit;
8092 }
8093
8094
8095
8096 /**
8097 * Handles deletion prompt with nonce validation
8098 */
8099 public function mxchat_handle_delete_prompt() {
8100 // Sanitize and validate nonce
8101 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
8102 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
8103 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
8104 }
8105
8106 // Check permissions
8107 if (!current_user_can('manage_options')) {
8108 wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
8109 }
8110
8111 // Get ID and source parameters
8112 $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
8113 $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
8114
8115 if (empty($id)) {
8116 wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
8117 }
8118
8119 $success = false;
8120 $error_message = '';
8121
8122 // Check if Pinecone is enabled and determine source automatically if not specified
8123 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
8124 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
8125
8126 // If source is not specified, determine based on Pinecone configuration
8127 if (empty($source)) {
8128 $source = $use_pinecone ? 'pinecone' : 'wordpress';
8129 }
8130
8131 if ($source === 'pinecone' || $use_pinecone) {
8132 //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
8133
8134 // Handle Pinecone deletion
8135 if (empty($pinecone_options['mxchat_pinecone_host']) ||
8136 empty($pinecone_options['mxchat_pinecone_api_key'])) {
8137 wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
8138 }
8139
8140 // Delete from Pinecone using the vector ID directly
8141 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
8142 $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
8143 $id,
8144 $pinecone_options['mxchat_pinecone_api_key'],
8145 $pinecone_options['mxchat_pinecone_host']
8146 );
8147 if ($result['success']) {
8148 $success = true;
8149
8150 // Remove from vector cache
8151 $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
8152 $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
8153
8154 set_transient('mxchat_admin_notice_success',
8155 esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
8156 } else {
8157 $error_message = $result['message'];
8158 set_transient('mxchat_admin_notice_error',
8159 esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
8160 }
8161 } else {
8162 //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
8163
8164 // Handle WordPress database deletion
8165 global $wpdb;
8166 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
8167
8168 // Clear cache and delete prompt
8169 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
8170
8171 $result = $wpdb->delete(
8172 $table_name,
8173 array('id' => intval($id)),
8174 array('%d')
8175 );
8176
8177 if ($result !== false) {
8178 $success = true;
8179 set_transient('mxchat_admin_notice_success',
8180 esc_html__('Entry deleted successfully.', 'mxchat'), 30);
8181 } else {
8182 set_transient('mxchat_admin_notice_error',
8183 esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
8184 }
8185 }
8186
8187 // Redirect back to the prompts page
8188 wp_safe_redirect(add_query_arg(
8189 array(
8190 'page' => 'mxchat-prompts',
8191 'deleted' => $success ? 'true' : 'false'
8192 ),
8193 admin_url('admin.php')
8194 ));
8195 exit;
8196 }
8197
8198
8199
8200
8201 /**
8202 * Averages multiple vectors into a single vector
8203 */
8204 private function mxchat_average_vectors($vectors) {
8205 $vector_length = count($vectors[0]);
8206 $sum_vector = array_fill(0, $vector_length, 0);
8207
8208 foreach ($vectors as $vector) {
8209 for ($i = 0; $i < $vector_length; $i++) {
8210 $sum_vector[$i] += $vector[$i];
8211 }
8212 }
8213
8214 // Divide each component by the number of vectors to get the average
8215 $num_vectors = count($vectors);
8216 for ($i = 0; $i < $vector_length; $i++) {
8217 $sum_vector[$i] /= $num_vectors;
8218 }
8219
8220 return $sum_vector;
8221 }
8222
8223
8224
8225
8226 /**
8227 * Generates embeddings from input text for MXChat
8228 */
8229 public function mxchat_generate_embedding($text) {
8230 // Enable detailed logging for debugging
8231 //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
8232 //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
8233
8234 $options = get_option('mxchat_options');
8235 $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
8236 //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
8237
8238 // Determine provider and endpoint
8239 if (strpos($selected_model, 'voyage') === 0) {
8240 $api_key = $options['voyage_api_key'] ?? '';
8241 $endpoint = 'https://api.voyageai.com/v1/embeddings';
8242 $provider_name = 'Voyage AI';
8243 //error_log('[MXCHAT-EMBED] Using Voyage AI API');
8244 } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
8245 $api_key = $options['gemini_api_key'] ?? '';
8246 $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
8247 $provider_name = 'Google Gemini';
8248 //error_log('[MXCHAT-EMBED] Using Google Gemini API');
8249 } else {
8250 $api_key = $options['api_key'] ?? '';
8251 $endpoint = 'https://api.openai.com/v1/embeddings';
8252 $provider_name = 'OpenAI';
8253 //error_log('[MXCHAT-EMBED] Using OpenAI API');
8254 }
8255
8256 //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
8257
8258 if (empty($api_key)) {
8259 $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
8260 //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
8261 return $error_message;
8262 }
8263
8264 // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
8265 $estimated_tokens = ceil(str_word_count($text) / 0.75);
8266 //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
8267
8268 if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
8269 //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
8270 // Consider truncating text here
8271 }
8272
8273 // Prepare request body based on provider
8274 if (strpos($selected_model, 'gemini-embedding') === 0) {
8275 // Gemini API format
8276 $request_body = array(
8277 'model' => 'models/' . $selected_model,
8278 'content' => array(
8279 'parts' => array(
8280 array('text' => $text)
8281 )
8282 )
8283 );
8284
8285 // Set output dimensionality to 1536 for consistency with other models
8286 $request_body['outputDimensionality'] = 1536;
8287 } else {
8288 // OpenAI/Voyage API format
8289 $request_body = array(
8290 'model' => $selected_model,
8291 'input' => $text
8292 );
8293
8294 // Add output_dimension for voyage-3-large model
8295 if ($selected_model === 'voyage-3-large') {
8296 $request_body['output_dimension'] = 2048;
8297 }
8298 }
8299
8300 //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
8301
8302 // Prepare headers based on provider
8303 if (strpos($selected_model, 'gemini-embedding') === 0) {
8304 // Gemini uses API key as query parameter
8305 $endpoint .= '?key=' . $api_key;
8306 $headers = array(
8307 'Content-Type' => 'application/json'
8308 );
8309 } else {
8310 // OpenAI/Voyage use Bearer token
8311 $headers = array(
8312 'Authorization' => 'Bearer ' . $api_key,
8313 'Content-Type' => 'application/json'
8314 );
8315 }
8316
8317 // Make API request
8318 //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
8319 $response = wp_remote_post($endpoint, array(
8320 'body' => wp_json_encode($request_body),
8321 'headers' => $headers,
8322 'timeout' => 60 // Increased timeout for large inputs
8323 ));
8324
8325 // Handle wp_remote_post errors
8326 if (is_wp_error($response)) {
8327 $error_message = $response->get_error_message();
8328 //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
8329 return 'Connection error: ' . $error_message;
8330 }
8331
8332 // Get and check HTTP response code
8333 $http_code = wp_remote_retrieve_response_code($response);
8334 //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
8335
8336 if ($http_code !== 200) {
8337 $error_body = wp_remote_retrieve_body($response);
8338 //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
8339
8340 // Try to parse error for more details
8341 $error_json = json_decode($error_body, true);
8342 if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
8343 $error_type = $error_json['error']['type'] ?? 'unknown';
8344 $error_message = $error_json['error']['message'] ?? 'No message';
8345 //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
8346 //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
8347
8348 // Customize error message for common API errors
8349 if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
8350 $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
8351 } elseif ($error_type === 'authentication_error') {
8352 $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
8353 }
8354
8355 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
8356 return $error_message;
8357 }
8358
8359 $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
8360 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
8361 return $error_message;
8362 }
8363
8364 // Parse response body
8365 $response_body = wp_remote_retrieve_body($response);
8366 //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
8367
8368 $response_data = json_decode($response_body, true);
8369
8370 if (json_last_error() !== JSON_ERROR_NONE) {
8371 $error = json_last_error_msg();
8372 //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
8373 //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
8374 return "Failed to parse API response: $error";
8375 }
8376
8377 // Handle different response formats based on provider
8378 if (strpos($selected_model, 'gemini-embedding') === 0) {
8379 // Gemini API response format
8380 if (isset($response_data['embedding']['values'])) {
8381 $embedding_dimensions = count($response_data['embedding']['values']);
8382 //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
8383
8384 // Check if embedding dimensions are as expected (should be 1536)
8385 if ($embedding_dimensions !== 1536) {
8386 //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
8387 }
8388
8389 return $response_data['embedding']['values'];
8390 } else {
8391 //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
8392 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
8393
8394 if (isset($response_data['error'])) {
8395 $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
8396 //error_log('[MXCHAT-EMBED] ' . $error_message);
8397 return $error_message;
8398 }
8399
8400 $error_message = "Invalid Gemini API response format: No embedding found";
8401 //error_log('[MXCHAT-EMBED] ' . $error_message);
8402 return $error_message;
8403 }
8404 } else {
8405 // OpenAI/Voyage API response format
8406 if (isset($response_data['data'][0]['embedding'])) {
8407 $embedding_dimensions = count($response_data['data'][0]['embedding']);
8408 //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
8409
8410 // Check if embedding dimensions are as expected
8411 if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
8412 ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
8413 //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
8414 }
8415
8416 return $response_data['data'][0]['embedding'];
8417 } else {
8418 //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
8419 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
8420
8421 if (isset($response_data['error'])) {
8422 $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
8423 //error_log('[MXCHAT-EMBED] ' . $error_message);
8424 return $error_message;
8425 }
8426
8427 $error_message = "Invalid API response format: No embedding found";
8428 //error_log('[MXCHAT-EMBED] ' . $error_message);
8429 return $error_message;
8430 }
8431 }
8432 }
8433
8434
8435
8436 }
8437 ?>
8438