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

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

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