PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.3.9
MxChat – AI Chatbot & Content Generation for WordPress v2.3.9
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / class-mxchat-admin.php

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

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