PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.1
MxChat – AI Chatbot & Content Generation for WordPress v2.5.1
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.1, at includes/class-mxchat-admin.php

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