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

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