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

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

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