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

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