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

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