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

6,741 lines 309.3 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 <?php if ($data_source === 'pinecone') : ?>
1817 <!-- AJAX Delete for Pinecone -->
1818 <button type="button"
1819 class="mxchat-button-icon delete-button-ajax"
1820 data-vector-id="<?php echo esc_attr($prompt->id); ?>"
1821 data-nonce="<?php echo wp_create_nonce('mxchat_delete_pinecone_prompt_nonce'); ?>"
1822 title="<?php esc_attr_e('Delete entry', 'mxchat'); ?>">
1823 <span class="dashicons dashicons-trash"></span>
1824 </button>
1825 <?php else : ?>
1826 <!-- Regular link delete for WordPress DB -->
1827 <a href="<?php echo esc_url(admin_url(
1828 'admin-post.php?action=mxchat_delete_prompt&id=' . esc_attr($prompt->id)
1829 . '&_wpnonce=' . wp_create_nonce('mxchat_delete_prompt_nonce')
1830 )); ?>"
1831 class="mxchat-button-icon delete-button"
1832 onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this entry?', 'mxchat'); ?>');">
1833 <span class="dashicons dashicons-trash"></span>
1834 </a>
1835 <?php endif; ?>
1836 </td>
1837 </tr>
1838 <?php endforeach; ?>
1839 <?php else : ?>
1840 <tr>
1841 <td colspan="<?php echo $data_source === 'pinecone' ? '5' : '4'; ?>" class="mxchat-no-records">
1842 <?php if ($use_pinecone) : ?>
1843 <?php esc_html_e('No vectors found in Pinecone database.', 'mxchat'); ?>
1844 <?php else : ?>
1845 <?php esc_html_e('No knowledge base entries found.', 'mxchat'); ?>
1846 <?php endif; ?>
1847 </td>
1848 </tr>
1849 <?php endif; ?>
1850 </tbody>
1851 </table>
1852 </div>
1853
1854 <?php if ($page_links) : ?>
1855 <div class="mxchat-pagination">
1856 <?php echo wp_kses_post($page_links); ?>
1857 </div>
1858 <?php endif; ?>
1859 </div>
1860
1861 </div>
1862
1863 <!-- Sync Settings Tab (Initially Hidden) -->
1864 <div id="mxchat-kb-tab-sync" class="mxchat-kb-tab-content">
1865 <div class="mxchat-card">
1866 <!-- Auto-Sync Settings -->
1867 <div class="mxchat-settings-section">
1868 <h3><?php esc_html_e('Auto-Sync Settings', 'mxchat'); ?></h3>
1869 <p class="mxchat-description">
1870 <?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'); ?>
1871 </p>
1872 <div class="mxchat-autosave-section">
1873 <div class="mxchat-toggle-group">
1874 <div class="mxchat-toggle-container">
1875 <label class="mxchat-toggle-switch">
1876 <input type="checkbox"
1877 name="mxchat_auto_sync_posts"
1878 class="mxchat-autosave-field"
1879 value="1"
1880 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1881 <?php checked(get_option('mxchat_auto_sync_posts', '0'), '1'); ?>>
1882 <span class="mxchat-toggle-slider"></span>
1883 </label>
1884 <span class="mxchat-toggle-label">
1885 <?php esc_html_e('Auto-sync Posts', 'mxchat'); ?>
1886 </span>
1887 </div>
1888
1889 <div class="mxchat-toggle-container">
1890 <label class="mxchat-toggle-switch">
1891 <input type="checkbox"
1892 name="mxchat_auto_sync_pages"
1893 class="mxchat-autosave-field"
1894 value="1"
1895 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1896 <?php checked(get_option('mxchat_auto_sync_pages', '0'), '1'); ?>>
1897 <span class="mxchat-toggle-slider"></span>
1898 </label>
1899 <span class="mxchat-toggle-label">
1900 <?php esc_html_e('Auto-sync Pages', 'mxchat'); ?>
1901 </span>
1902 </div>
1903
1904 <!-- Custom Post Types Section -->
1905 <div class="mxchat-section-content">
1906 <div class="mxchat-custom-post-types-header">
1907 <button id="mxchat-custom-post-types-toggle" class="mxchat-button-secondary">
1908 <?php esc_html_e('Advanced Custom Post Sync Settings', 'mxchat'); ?>
1909 <span class="mxchat-toggle-icon"></span>
1910 </button>
1911 </div>
1912
1913 <div id="mxchat-custom-post-types-container" class="mxchat-custom-post-types-container" style="display: none;">
1914 <h3><?php esc_html_e('Sync Custom Post Types', 'mxchat'); ?></h3>
1915 <p><?php esc_html_e('Select additional custom post types to automatically sync with the chatbot.', 'mxchat'); ?></p>
1916
1917 <div class="mxchat-custom-post-types">
1918 <?php
1919 $post_types = $knowledge_manager->mxchat_get_public_post_types();
1920 // Skip post and page as they're handled separately
1921 unset($post_types['post']);
1922 unset($post_types['page']);
1923
1924 if (!empty($post_types)) {
1925 foreach ($post_types as $post_type => $label) {
1926 $option_name = 'mxchat_auto_sync_' . $post_type;
1927 $is_enabled = get_option($option_name, '0');
1928 ?>
1929 <div class="mxchat-toggle-container">
1930 <label class="mxchat-toggle-switch">
1931 <input type="checkbox"
1932 name="<?php echo esc_attr($option_name); ?>"
1933 class="mxchat-autosave-field"
1934 value="1"
1935 data-nonce="<?php echo wp_create_nonce('mxchat_prompts_setting_nonce'); ?>"
1936 <?php checked($is_enabled, '1'); ?>>
1937 <span class="mxchat-toggle-slider"></span>
1938 </label>
1939 <span class="mxchat-toggle-label">
1940 <?php echo esc_html($label); ?> (<?php echo esc_html($post_type); ?>)
1941 </span>
1942 </div>
1943 <?php
1944 }
1945 } else {
1946 echo '<p>' . esc_html__('No custom post types found.', 'mxchat') . '</p>';
1947 }
1948 ?>
1949 </div>
1950 </div>
1951 </div>
1952 </div>
1953 </div>
1954 </div>
1955 </div>
1956 </div>
1957
1958 <!-- NEW PINECONE TAB -->
1959 <div id="mxchat-kb-tab-pinecone" class="mxchat-kb-tab-content mxchat-autosave-section">
1960 <div class="mxchat-card">
1961 <h2><?php esc_html_e('Pinecone Vector Database Settings', 'mxchat'); ?></h2>
1962 <p class="mxchat-description">
1963 <?php echo wp_kses(
1964 __('<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'),
1965 array('strong' => array())
1966 ); ?>
1967 </p>
1968
1969 <div class="mxchat-pinecone-info-box">
1970 <div class="mxchat-info-icon">
1971 <span class="dashicons dashicons-info"></span>
1972 </div>
1973 <div class="mxchat-info-content">
1974 <h4><?php esc_html_e('What is Pinecone?', 'mxchat'); ?></h4>
1975 <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>
1976 <p><a href="https://www.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Learn more about Pinecone →', 'mxchat'); ?></a></p>
1977 </div>
1978 </div>
1979
1980 <div class="mxchat-database-settings-form">
1981 <!-- REMOVED the WordPress form - we're using AJAX auto-save instead -->
1982 <?php
1983 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
1984 $use_pinecone = $pinecone_options['mxchat_use_pinecone'] ?? '0';
1985 ?>
1986
1987 <div class="mxchat-toggle-container">
1988 <label class="mxchat-toggle-switch">
1989 <input type="checkbox"
1990 name="mxchat_pinecone_addon_options[mxchat_use_pinecone]"
1991 value="1"
1992 <?php checked($use_pinecone, '1'); ?>>
1993 <span class="mxchat-toggle-slider"></span>
1994 </label>
1995 <span class="mxchat-toggle-label">
1996 <?php esc_html_e('Enable Pinecone Database', 'mxchat'); ?>
1997 </span>
1998 </div>
1999
2000 <div class="mxchat-pinecone-settings" <?php echo $use_pinecone ? '' : 'style="display: none;"'; ?>>
2001
2002 <?php if ($use_pinecone) : ?>
2003 <div class="mxchat-knowledge-warning success">
2004 <p><span class="dashicons dashicons-yes-alt"></span>
2005 <?php esc_html_e('Pinecone is enabled. All new knowledge base content will be stored in Pinecone.', 'mxchat'); ?>
2006 </p>
2007 </div>
2008 <?php endif; ?>
2009
2010 <div class="mxchat-form-group">
2011 <label for="mxchat_pinecone_api_key">
2012 <?php esc_html_e('Pinecone API Key', 'mxchat'); ?> <span class="required">*</span>
2013 </label>
2014 <input type="password"
2015 id="mxchat_pinecone_api_key"
2016 name="mxchat_pinecone_addon_options[mxchat_pinecone_api_key]"
2017 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_api_key'] ?? ''); ?>"
2018 class="regular-text"
2019 placeholder="pcsk_..." />
2020 <p class="description">
2021 <?php esc_html_e('Found in your Pinecone dashboard under API Keys.', 'mxchat'); ?>
2022 <a href="https://app.pinecone.io/" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Open Pinecone Dashboard', 'mxchat'); ?></a>
2023 </p>
2024 </div>
2025
2026 <div class="mxchat-form-group">
2027 <label for="mxchat_pinecone_environment">
2028 <?php esc_html_e('Region', 'mxchat'); ?>
2029 </label>
2030 <input type="text"
2031 id="mxchat_pinecone_environment"
2032 name="mxchat_pinecone_addon_options[mxchat_pinecone_environment]"
2033 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_environment'] ?? ''); ?>"
2034 placeholder="e.g., gcp-starter"
2035 class="regular-text" />
2036 <p class="description">
2037 <?php esc_html_e('Your Pinecone environment/region (e.g., gcp-starter, us-west1-gcp, us-east-1-aws)', 'mxchat'); ?>
2038 </p>
2039 </div>
2040
2041 <div class="mxchat-form-group">
2042 <label for="mxchat_pinecone_index">
2043 <?php esc_html_e('Index Name', 'mxchat'); ?> <span class="required">*</span>
2044 </label>
2045 <input type="text"
2046 id="mxchat_pinecone_index"
2047 name="mxchat_pinecone_addon_options[mxchat_pinecone_index]"
2048 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_index'] ?? ''); ?>"
2049 placeholder="e.g., my-wordpress-vectors"
2050 class="regular-text" />
2051 <p class="description">
2052 <?php esc_html_e('The name of your Pinecone index. Must be created in your Pinecone dashboard first.', 'mxchat'); ?>
2053 </p>
2054 </div>
2055
2056 <div class="mxchat-form-group">
2057 <label for="mxchat_pinecone_host">
2058 <?php esc_html_e('Pinecone Host', 'mxchat'); ?> <span class="required">*</span>
2059 </label>
2060 <input type="text"
2061 id="mxchat_pinecone_host"
2062 name="mxchat_pinecone_addon_options[mxchat_pinecone_host]"
2063 value="<?php echo esc_attr($pinecone_options['mxchat_pinecone_host'] ?? ''); ?>"
2064 placeholder="e.g., my-index-xyz123.svc.pinecone.io"
2065 class="regular-text" />
2066 <p class="description">
2067 <?php esc_html_e('The hostname from your Pinecone index URL (exclude https://). Found in your index details.', 'mxchat'); ?>
2068 </p>
2069 </div>
2070
2071 <div class="mxchat-setup-steps">
2072 <h3><?php esc_html_e('Setup Instructions', 'mxchat'); ?></h3>
2073 <div class="mxchat-setup-step">
2074 <span class="step-number">1</span>
2075 <div class="step-content">
2076 <h4><?php esc_html_e('Create Account', 'mxchat'); ?></h4>
2077 <p><?php esc_html_e('Create a free account at', 'mxchat'); ?> <a href="https://www.pinecone.io/" target="_blank">pinecone.io</a></p>
2078 </div>
2079 </div>
2080 <div class="mxchat-setup-step">
2081 <span class="step-number">2</span>
2082 <div class="step-content">
2083 <h4><?php esc_html_e('Create Index', 'mxchat'); ?></h4>
2084 <p><?php esc_html_e('Create a new index with these settings:', 'mxchat'); ?></p>
2085 <ul>
2086 <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>
2087 <li><?php esc_html_e('Metric: Cosine', 'mxchat'); ?></li>
2088 <li><?php esc_html_e('Cloud & Region: Your preferred region', 'mxchat'); ?></li>
2089 </ul>
2090 </div>
2091 </div>
2092 <div class="mxchat-setup-step">
2093 <span class="step-number">3</span>
2094 <div class="step-content">
2095 <h4><?php esc_html_e('Get API Key', 'mxchat'); ?></h4>
2096 <p><?php esc_html_e('Copy your API key from the API Keys section', 'mxchat'); ?></p>
2097 </div>
2098 </div>
2099 <div class="mxchat-setup-step">
2100 <span class="step-number">4</span>
2101 <div class="step-content">
2102 <h4><?php esc_html_e('Get Index Host', 'mxchat'); ?></h4>
2103 <p><?php esc_html_e('Copy your index host URL from the index details', 'mxchat'); ?></p>
2104 </div>
2105 </div>
2106 <div class="mxchat-setup-step">
2107 <span class="step-number">5</span>
2108 <div class="step-content">
2109 <h4><?php esc_html_e('Save Settings', 'mxchat'); ?></h4>
2110 <p><?php esc_html_e('Fill in the form above and save settings', 'mxchat'); ?></p>
2111 </div>
2112 </div>
2113 </div>
2114
2115
2116
2117 </div>
2118
2119
2120 </div>
2121 </div>
2122 </div>
2123
2124 </div>
2125
2126
2127
2128
2129 </div>
2130 </div>
2131
2132
2133 <!-- Content Selector Modal -->
2134 <div id="mxchat-kb-content-selector-modal" class="mxchat-kb-modal">
2135 <div class="mxchat-kb-modal-content">
2136 <div class="mxchat-kb-modal-header">
2137 <h3>
2138 <?php esc_html_e('Select WordPress Content', 'mxchat'); ?><br>
2139 <span class="mxchat-kb-header-note"><?php esc_html_e('(Content imported here will be tagged "In Knowledge Base")', 'mxchat'); ?></span>
2140 </h3>
2141 <span class="mxchat-kb-modal-close">&times;</span>
2142 </div>
2143 <div class="mxchat-kb-modal-filters">
2144 <div class="mxchat-kb-search-group">
2145 <input type="text" id="mxchat-kb-content-search" placeholder="<?php esc_attr_e('Search...', 'mxchat'); ?>">
2146 </div>
2147
2148 <div class="mxchat-kb-filter-group">
2149 <select id="mxchat-kb-content-type-filter">
2150 <option value="all"><?php esc_html_e('All Content Types', 'mxchat'); ?></option>
2151 <option value="post"><?php esc_html_e('Posts', 'mxchat'); ?></option>
2152 <option value="page"><?php esc_html_e('Pages', 'mxchat'); ?></option>
2153 <?php
2154 // Add other post types dynamically
2155 $post_types = get_post_types(array('public' => true), 'objects');
2156 foreach ($post_types as $post_type) {
2157 // Skip post and page as they're already added
2158 if (!in_array($post_type->name, array('post', 'page'))) {
2159 echo '<option value="' . esc_attr($post_type->name) . '">' . esc_html($post_type->label) . '</option>';
2160 }
2161 }
2162 ?>
2163 </select>
2164
2165 <select id="mxchat-kb-content-status-filter">
2166 <option value="publish"><?php esc_html_e('Published', 'mxchat'); ?></option>
2167 <option value="draft"><?php esc_html_e('Drafts', 'mxchat'); ?></option>
2168 <option value="all"><?php esc_html_e('All Statuses', 'mxchat'); ?></option>
2169 </select>
2170
2171 <select id="mxchat-kb-processed-filter">
2172 <option value="all"><?php esc_html_e('All Content', 'mxchat'); ?></option>
2173 <option value="processed"><?php esc_html_e('In Knowledge Base', 'mxchat'); ?></option>
2174 <option value="unprocessed"><?php esc_html_e('Not In Knowledge Base', 'mxchat'); ?></option>
2175 </select>
2176 </div>
2177 </div>
2178
2179 <div class="mxchat-kb-content-selection">
2180 <div class="mxchat-kb-selection-header">
2181 <label>
2182 <input type="checkbox" id="mxchat-kb-select-all">
2183 <?php esc_html_e('Select All', 'mxchat'); ?>
2184 </label>
2185 <span class="mxchat-kb-selection-count">0 <?php esc_html_e('selected', 'mxchat'); ?></span>
2186 </div>
2187
2188 <div class="mxchat-kb-content-list">
2189 <!-- Content will be loaded here via AJAX -->
2190 <div class="mxchat-kb-loading">
2191 <span class="mxchat-kb-spinner is-active"></span>
2192 <?php esc_html_e('Loading content...', 'mxchat'); ?>
2193 </div>
2194 </div>
2195
2196 <div class="mxchat-kb-pagination">
2197 <!-- Pagination will be added here -->
2198 </div>
2199 </div>
2200
2201 <div class="mxchat-kb-modal-footer">
2202 <button type="button" id="mxchat-kb-process-selected" class="mxchat-kb-button-primary" disabled>
2203 <?php esc_html_e('Process Selected Content', 'mxchat'); ?>
2204 <span class="mxchat-kb-selected-count">(0)</span>
2205 </button>
2206 <button type="button" class="mxchat-kb-button-secondary mxchat-kb-modal-close">
2207 <?php esc_html_e('Cancel', 'mxchat'); ?>
2208 </button>
2209 </div>
2210 </div>
2211 </div>
2212
2213 <?php
2214 }
2215 public function mxchat_delete_chat_history() {
2216 if (!current_user_can('manage_options')) {
2217 echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]);
2218 wp_die();
2219 }
2220 check_ajax_referer('mxchat_delete_chat_history', 'security');
2221 global $wpdb;
2222 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
2223
2224 if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) {
2225 $deleted_count = 0;
2226
2227 foreach ($_POST['delete_session_ids'] as $session_id) {
2228 $session_id_sanitized = sanitize_text_field($session_id);
2229
2230 // Clear relevant cache before deletion
2231 $cache_key = 'chat_session_' . $session_id_sanitized;
2232 wp_cache_delete($cache_key, 'mxchat_chat_sessions');
2233
2234 // Perform the deletion from the database table
2235 $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]);
2236
2237 // Delete the corresponding option entry from wp_options table
2238 delete_option("mxchat_history_" . $session_id_sanitized);
2239
2240 // Delete any associated metadata options
2241 delete_option("mxchat_email_" . $session_id_sanitized);
2242 delete_option("mxchat_agent_name_" . $session_id_sanitized);
2243
2244 $deleted_count++;
2245 }
2246
2247 // Optionally, clear a general cache if you have one
2248 wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions');
2249
2250 echo wp_json_encode([
2251 'success' => sprintf(
2252 esc_html__('%d chat session(s) have been deleted from all storage locations.', 'mxchat'),
2253 $deleted_count
2254 )
2255 ]);
2256 } else {
2257 echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]);
2258 }
2259
2260 wp_die();
2261 }
2262
2263 public function display_admin_notices() {
2264 // Check if we're on a MXChat admin page
2265 $screen = get_current_screen();
2266 if (!$screen || strpos($screen->base, 'mxchat') === false) {
2267 return;
2268 }
2269
2270 //error_log('MxChat admin_notices hook fired on screen: ' . $screen->base);
2271
2272 // Check for error notices
2273 $error_notice = get_transient('mxchat_admin_notice_error');
2274 if ($error_notice) {
2275 //error_log('Found error transient: ' . $error_notice);
2276 echo '<div class="notice notice-error is-dismissible"><p>' . wp_kses_post($error_notice) . '</p></div>';
2277 delete_transient('mxchat_admin_notice_error');
2278 //error_log('Displayed and deleted error transient');
2279 } else {
2280 //error_log('No error transient found');
2281 }
2282
2283 // Check for success notices
2284 $success_notice = get_transient('mxchat_admin_notice_success');
2285 if ($success_notice) {
2286 //error_log('Found success transient: ' . $success_notice);
2287 echo '<div class="notice notice-success is-dismissible"><p>' . wp_kses_post($success_notice) . '</p></div>';
2288 delete_transient('mxchat_admin_notice_success');
2289 //error_log('Displayed and deleted success transient');
2290 }
2291
2292 // Check for info notices
2293 $info_notice = get_transient('mxchat_admin_notice_info');
2294 if ($info_notice) {
2295 //error_log('Found info transient: ' . $info_notice);
2296 echo '<div class="notice notice-info is-dismissible"><p>' . wp_kses_post($info_notice) . '</p></div>';
2297 delete_transient('mxchat_admin_notice_info');
2298 //error_log('Displayed and deleted info transient');
2299 }
2300
2301 }
2302
2303 public function mxchat_create_activation_page() {
2304 $license_status = get_option('mxchat_license_status', 'inactive');
2305 $license_error = get_option('mxchat_license_error', '');
2306 ?>
2307 <div class="wrap mxchat-admin-activation">
2308 <div class="mxchat-pro-hero">
2309 <h1 class="pro-title">
2310 <span class="pro-gradient-text">Activate</span> MxChat Pro
2311 </h1>
2312 <p class="pro-subtitle">
2313 <?php esc_html_e('Enter your license key to unlock premium features, advanced AI capabilities, and priority support.', 'mxchat'); ?>
2314 </p>
2315 </div>
2316
2317 <?php if ($license_status === 'inactive' && !empty($license_error)): ?>
2318 <div class="error notice">
2319 <p><?php echo esc_html($license_error); ?></p>
2320 </div>
2321 <?php endif; ?>
2322
2323 <form id="mxchat-activation-form" class="mxchat-pro-form" style="<?php echo $license_status === 'active' ? 'display: none;' : ''; ?>">
2324 <div class="mxchat-pro-form-container">
2325 <table class="form-table">
2326 <tr valign="top">
2327 <th scope="row"><?php esc_html_e('Email Address', 'mxchat'); ?></th>
2328 <td>
2329 <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 />
2330 </td>
2331 </tr>
2332 <tr valign="top">
2333 <th scope="row"><?php esc_html_e('Activation Key', 'mxchat'); ?></th>
2334 <td>
2335 <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 />
2336 </td>
2337 </tr>
2338 </table>
2339 <?php if ($license_status !== 'active'): ?>
2340 <div class="mxchat-pro-button-container">
2341 <button type="submit" id="activate_license_button" class="button button-primary mxchat-pro-button"><?php esc_html_e('Activate License', 'mxchat'); ?></button>
2342 <div id="mxchat-activation-spinner" class="mxchat-activation-spinner" style="display: none;"></div>
2343 </div>
2344 <?php endif; ?>
2345 </div>
2346 </form>
2347 <!-- License Status Display -->
2348 <div class="mxchat-pro-status">
2349 <h3><?php esc_html_e('License Status:', 'mxchat'); ?>
2350 <span id="mxchat-license-status" class="mxchat-status-badge <?php echo $license_status; ?>">
2351 <?php echo $license_status === 'active' ? esc_html__('Active', 'mxchat') : esc_html__('Inactive', 'mxchat'); ?>
2352 </span>
2353 </h3>
2354 </div>
2355
2356 </div>
2357 <?php
2358 }
2359
2360 public function mxchat_actions_page_html() {
2361 if (!current_user_can('manage_options')) {
2362 return;
2363 }
2364
2365 // Keep existing data fetching logic
2366 global $wpdb;
2367 $table_name = $wpdb->prefix . 'mxchat_intents';
2368 $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
2369 $per_page = 20;
2370 $offset = ($page - 1) * $per_page;
2371
2372 // Success message
2373 if (isset($_GET['updated']) && $_GET['updated'] === 'true') {
2374 echo '<div class="notice notice-success is-dismissible"><p>' .
2375 esc_html__('Action updated successfully.', 'mxchat') .
2376 '</p></div>';
2377 }
2378
2379 // Filtering logic
2380 $where = '1=1';
2381 $search_term = isset($_GET['s']) ? trim($_GET['s']) : '';
2382 $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : '';
2383
2384 if ($search_term) {
2385 $search_term_like = '%' . $wpdb->esc_like($search_term) . '%';
2386 $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)',
2387 $search_term_like, $search_term_like);
2388 }
2389
2390 if ($callback_filter) {
2391 $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter);
2392 }
2393
2394 // Pagination
2395 $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where");
2396 $total_pages = ceil($total_intents / $per_page);
2397
2398 // Get intents (now called actions)
2399 $actions = $wpdb->get_results($wpdb->prepare(
2400 "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d",
2401 $per_page, $offset
2402 ));
2403
2404 // Get callbacks
2405 $available_callbacks = $this->mxchat_get_available_callbacks();
2406
2407 ?>
2408 <div class="wrap mxchat-wrapper">
2409 <!-- Hero Section -->
2410 <div class="mxchat-hero">
2411 <h1 class="mxchat-main-title">
2412 <span class="mxchat-gradient-text">Actions</span> Manager
2413 </h1>
2414 <p class="mxchat-hero-subtitle">
2415 <?php esc_html_e('Create and manage custom actions to enhance your chatbot\'s capabilities.', 'mxchat'); ?>
2416 </p>
2417 </div>
2418
2419 <!-- Actions Header with Search and Filter -->
2420 <div class="mxchat-actions-header">
2421 <div class="mxchat-actions-filters">
2422 <form method="get" class="mxchat-search-form">
2423 <input type="hidden" name="page" value="mxchat-actions">
2424 <div class="mxchat-search-group">
2425 <span class="dashicons dashicons-search"></span>
2426 <input type="text" name="s" class="mxchat-search-input"
2427 placeholder="<?php esc_attr_e('Search Actions', 'mxchat'); ?>"
2428 value="<?php echo esc_attr($search_term); ?>">
2429 </div>
2430 <select name="callback_filter" class="mxchat-action-filter">
2431 <option value=""><?php esc_html_e('All Action Types', 'mxchat'); ?></option>
2432 <?php foreach ($available_callbacks as $function => $callback_data) :
2433 $label = $callback_data['label']; ?>
2434 <option value="<?php echo esc_attr($function); ?>"
2435 <?php selected($callback_filter, $function); ?>>
2436 <?php echo esc_html($label); ?>
2437 </option>
2438 <?php endforeach; ?>
2439 </select>
2440 <button type="submit" class="mxchat-button-secondary">
2441 <?php esc_html_e('Filter', 'mxchat'); ?>
2442 </button>
2443 </form>
2444 </div>
2445 <div class="mxchat-actions-controls">
2446 <button type="button" id="mxchat-add-action-btn" class="mxchat-button-primary">
2447 <span class="dashicons dashicons-plus-alt"></span>
2448 <?php esc_html_e('Add New Action', 'mxchat'); ?>
2449 </button>
2450 </div>
2451 </div>
2452
2453 <!-- Actions Grid Layout - All actions in a single grid -->
2454 <div class="mxchat-actions-grid">
2455 <div class="mxchat-cards-container">
2456 <?php if (!empty($actions)) : ?>
2457 <?php foreach ($actions as $action) :
2458 $callback_function = $action->callback_function;
2459 $callback_label = isset($available_callbacks[$callback_function]['label'])
2460 ? $available_callbacks[$callback_function]['label']
2461 : $callback_function;
2462 $threshold_value = isset($action->similarity_threshold)
2463 ? round($action->similarity_threshold * 100)
2464 : 85;
2465
2466 // Check if this is a form action
2467 $is_form_action = strpos($action->intent_label, 'Form ') === 0;
2468
2469 // Get action status (enabled/disabled) - default to true if column doesn't exist
2470 $is_enabled = isset($action->enabled) ? (bool)$action->enabled : true;
2471 ?>
2472 <div class="mxchat-action-card <?php echo $is_form_action ? 'mxchat-form-action' : ''; ?>">
2473 <div class="mxchat-card-header">
2474 <div class="mxchat-card-title"><?php echo esc_html($action->intent_label); ?></div>
2475 <div class="mxchat-card-toggle">
2476 <label class="mxchat-switch">
2477 <input type="checkbox" class="mxchat-action-toggle"
2478 data-action-id="<?php echo esc_attr($action->id); ?>"
2479 <?php checked($is_enabled); ?>>
2480 <span class="mxchat-slider round"></span>
2481 </label>
2482 </div>
2483 </div>
2484
2485 <div class="mxchat-card-body">
2486 <div class="mxchat-card-description">
2487 <strong><?php esc_html_e('Type:', 'mxchat'); ?></strong>
2488 <?php echo esc_html($callback_label); ?>
2489 </div>
2490
2491 <div class="mxchat-card-phrases">
2492 <strong><?php esc_html_e('Trigger phrases:', 'mxchat'); ?></strong>
2493 <div class="mxchat-phrases-preview">
2494 <?php
2495 // Check if the helper function exists, otherwise use a simple substring
2496 if (method_exists($this, 'get_trimmed_phrases')) {
2497 echo esc_html($this->get_trimmed_phrases($action->phrases));
2498 } else {
2499 echo esc_html(strlen($action->phrases) > 100 ?
2500 substr($action->phrases, 0, 97) . '...' :
2501 $action->phrases);
2502 }
2503 ?>
2504 </div>
2505 </div>
2506
2507 <div class="mxchat-threshold-control">
2508 <div class="mxchat-threshold-label">
2509 <?php esc_html_e('Similarity Threshold:', 'mxchat'); ?>
2510 <span class="mxchat-threshold-value"><?php echo esc_html($threshold_value); ?>%</span>
2511 </div>
2512 </div>
2513 </div>
2514
2515 <div class="mxchat-card-footer">
2516 <?php
2517 // Check if it's a form action
2518 $is_form_action = preg_match('/Form (\d+)/', $action->intent_label, $form_matches);
2519
2520 // Check if it's a recommendation flow action
2521 $is_flow_action = preg_match('/Recommendation Flow (\d+)/', $action->intent_label, $flow_matches);
2522
2523 if ($is_form_action) {
2524 $form_id = isset($form_matches[1]) ? $form_matches[1] : '';
2525 ?>
2526 <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-forms&action=edit&form_id=' . $form_id)); ?>"
2527 class="mxchat-button-primary">
2528 <span class="dashicons dashicons-feedback"></span>
2529 <?php esc_html_e('Edit Form', 'mxchat'); ?>
2530 </a>
2531 <?php } elseif ($is_flow_action) {
2532 ?>
2533 <a href="<?php echo esc_url(admin_url('admin.php?page=mxchat-smart-recommender')); ?>"
2534 class="mxchat-button-primary">
2535 <span class="dashicons dashicons-list-view"></span>
2536 <?php esc_html_e('Manage Flows', 'mxchat'); ?>
2537 </a>
2538 <?php } else { ?>
2539 <button type="button"
2540 class="mxchat-button-secondary mxchat-edit-button"
2541 data-action-id="<?php echo esc_attr($action->id); ?>"
2542 data-phrases="<?php echo esc_attr($action->phrases); ?>"
2543 data-label="<?php echo esc_attr($action->intent_label); ?>"
2544 data-threshold="<?php echo esc_attr(round($action->similarity_threshold * 100)); ?>"
2545 data-callback-function="<?php echo esc_attr($action->callback_function); ?>">
2546 <span class="dashicons dashicons-edit"></span>
2547 <?php esc_html_e('Edit', 'mxchat'); ?>
2548 </button>
2549 <form method="post"
2550 action="<?php echo esc_url(admin_url('admin-post.php')); ?>"
2551 class="mxchat-delete-form"
2552 onsubmit="return confirm('<?php esc_attr_e('Are you sure you want to delete this action?', 'mxchat'); ?>');">
2553 <?php wp_nonce_field('mxchat_delete_intent_nonce'); ?>
2554 <input type="hidden" name="action" value="mxchat_delete_intent">
2555 <input type="hidden" name="intent_id" value="<?php echo esc_attr($action->id); ?>">
2556 <button type="submit" class="mxchat-button-text mxchat-delete-button">
2557 <span class="dashicons dashicons-trash"></span>
2558 <?php esc_html_e('Delete', 'mxchat'); ?>
2559 </button>
2560 </form>
2561 <?php } ?>
2562 </div>
2563
2564
2565 </div>
2566 <?php endforeach; ?>
2567 <?php else : ?>
2568 <!-- If no actions found -->
2569 <div class="mxchat-no-actions">
2570 <div class="mxchat-empty-state">
2571 <span class="dashicons dashicons-format-chat"></span>
2572 <h2><?php esc_html_e('No actions found', 'mxchat'); ?></h2>
2573 <p><?php esc_html_e('Get started by creating your first action to enhance your chatbot.', 'mxchat'); ?></p>
2574 <button type="button" id="mxchat-create-first-action" class="mxchat-button-primary">
2575 <?php esc_html_e('Create Your First Action', 'mxchat'); ?>
2576 </button>
2577 </div>
2578 </div>
2579 <?php endif; ?>
2580 </div>
2581 </div>
2582
2583 <?php if ($total_pages > 1) : ?>
2584 <div class="mxchat-pagination">
2585 <?php
2586 echo paginate_links(array(
2587 'base' => add_query_arg('paged', '%#%'),
2588 'format' => '',
2589 'prev_text' => __('&laquo; Previous', 'mxchat'),
2590 'next_text' => __('Next &raquo;', 'mxchat'),
2591 'total' => $total_pages,
2592 'current' => $page
2593 ));
2594 ?>
2595 </div>
2596 <?php endif; ?>
2597
2598 <!-- Add/Edit Action Modal with Step-Based Approach -->
2599 <!-- Complete Modal HTML with Defined Groups Variable -->
2600 <div id="mxchat-action-modal" class="mxchat-modal" style="display: none;">
2601 <div class="mxchat-modal-content">
2602 <span class="mxchat-modal-close">&times;</span>
2603
2604 <form id="mxchat-action-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
2605 <!-- Dynamic nonce field -->
2606 <div id="action-nonce-container">
2607 <?php wp_nonce_field('mxchat_add_intent_nonce', 'add_intent_nonce'); ?>
2608 </div>
2609 <input type="hidden" name="action" id="form_action_type" value="mxchat_add_intent">
2610 <input type="hidden" name="intent_id" id="edit_action_id" value="">
2611 <input type="hidden" name="callback_function" id="callback_function" value="">
2612
2613 <!-- Step 1: Action Type Selection -->
2614 <div id="mxchat-action-step-1" class="mxchat-action-step active">
2615 <div class="mxchat-step-indicator">
2616 <div class="mxchat-step-number">1</div>
2617 <div class="mxchat-step-title"><?php esc_html_e('Select Action Type', 'mxchat'); ?></div>
2618 </div>
2619
2620 <div id="mxchat-action-type-selector" class="mxchat-action-type-selector">
2621 <div class="mxchat-action-type-search">
2622 <span class="dashicons dashicons-search"></span>
2623 <input type="text" id="action-type-search" placeholder="<?php esc_attr_e('Search action types...', 'mxchat'); ?>" class="mxchat-action-type-search-input">
2624 </div>
2625
2626 <?php
2627 // Get the callbacks - IMPORTANT: Define the $groups variable here
2628 $groups = $this->mxchat_get_available_callbacks(true, true);
2629 ?>
2630
2631 <div class="mxchat-action-type-categories">
2632 <button type="button" class="mxchat-category-button active" data-category="all"><?php esc_html_e('All', 'mxchat'); ?></button>
2633 <?php
2634 // Get unique categories from the defined groups
2635 foreach ($groups as $group_label => $group_callbacks) :
2636 $category_slug = sanitize_title($group_label);
2637 ?>
2638 <button type="button" class="mxchat-category-button" data-category="<?php echo esc_attr($category_slug); ?>"><?php echo esc_html($group_label); ?></button>
2639 <?php endforeach; ?>
2640 </div>
2641
2642 <div class="mxchat-action-types-grid">
2643 <?php
2644 // Generate action cards from available callbacks
2645 foreach ($groups as $group_label => $group_callbacks) :
2646 $category_slug = sanitize_title($group_label);
2647
2648 foreach ($group_callbacks as $function => $data) :
2649 $label = $data['label'];
2650 $pro_only = $data['pro_only'];
2651 $icon = isset($data['icon']) ? $data['icon'] : 'admin-generic';
2652 $description = isset($data['description']) ? $data['description'] : '';
2653 $is_addon = isset($data['addon']) && $data['addon'] !== false;
2654 $addon_name = isset($data['addon_name']) ? $data['addon_name'] : '';
2655 $is_installed = isset($data['installed']) ? $data['installed'] : true;
2656
2657 // Determine card status and styling
2658 $card_class = 'mxchat-action-type-card';
2659 $icon_class = 'mxchat-action-type-icon';
2660 $status_badge = '';
2661
2662 if ($pro_only && !$this->is_activated) {
2663 // Pro feature but no Pro license
2664 $icon_class .= ' pro-feature';
2665 $status_badge = '<span class="mxchat-pro-badge">' . esc_html__('Pro', 'mxchat') . '</span>';
2666 }
2667
2668 if ($is_addon && !$is_installed) {
2669 // Add-on not installed
2670 $card_class .= ' not-installed';
2671 $status_badge .= '<span class="mxchat-addon-badge">' . esc_html__('Add-on Required', 'mxchat') . '</span>';
2672 }
2673
2674 // Default description if none provided
2675 if (empty($description)) {
2676 $description = sprintf(
2677 esc_html__('Use the %s action in your chatbot', 'mxchat'),
2678 $label
2679 );
2680 }
2681 ?>
2682 <div class="<?php echo esc_attr($card_class); ?>"
2683 data-category="<?php echo esc_attr($category_slug); ?>"
2684 data-value="<?php echo esc_attr($function); ?>"
2685 data-label="<?php echo esc_attr($label); ?>"
2686 data-pro="<?php echo $pro_only ? 'true' : 'false'; ?>"
2687 data-addon="<?php echo esc_attr($is_addon ? $data['addon'] : ''); ?>"
2688 data-installed="<?php echo $is_installed ? 'true' : 'false'; ?>">
2689 <div class="<?php echo esc_attr($icon_class); ?>">
2690 <span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span>
2691 </div>
2692 <div class="mxchat-action-type-info">
2693 <h4><?php echo esc_html($label); ?></h4>
2694 <p><?php echo esc_html($description); ?></p>
2695 <?php if (!empty($status_badge)) : ?>
2696 <?php echo $status_badge; ?>
2697 <?php endif; ?>
2698
2699 <?php if ($is_addon && !$is_installed) : ?>
2700 <div class="mxchat-addon-info">
2701 <?php echo esc_html(sprintf(
2702 __('Requires %s', 'mxchat'),
2703 $addon_name
2704 )); ?>
2705 </div>
2706 <?php endif; ?>
2707 </div>
2708 </div>
2709 <?php endforeach;
2710 endforeach; ?>
2711 </div>
2712 </div>
2713
2714 <div class="mxchat-modal-actions">
2715 <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2716 <?php esc_html_e('Cancel', 'mxchat'); ?>
2717 </button>
2718 </div>
2719 </div>
2720
2721 <!-- Step 2: Action Configuration -->
2722 <div id="mxchat-action-step-2" class="mxchat-action-step">
2723 <div class="mxchat-step-indicator">
2724 <div class="mxchat-step-number">2</div>
2725 <div class="mxchat-step-title"><?php esc_html_e('Configure Action', 'mxchat'); ?></div>
2726 </div>
2727
2728 <div class="mxchat-selected-action">
2729 <button type="button" class="mxchat-back-button" id="mxchat-back-to-step-1">
2730 <span class="dashicons dashicons-arrow-left-alt"></span>
2731 <?php esc_html_e('Back to Action Types', 'mxchat'); ?>
2732 </button>
2733 <div class="mxchat-selected-action-info">
2734 <div id="selected-action-icon" class="mxchat-action-type-icon">
2735 <span class="dashicons dashicons-admin-generic"></span>
2736 </div>
2737 <div class="mxchat-selected-action-details">
2738 <h3 id="selected-action-title"><?php esc_html_e('Selected Action', 'mxchat'); ?></h3>
2739 <p id="selected-action-description"><?php esc_html_e('Configure this action for your chatbot', 'mxchat'); ?></p>
2740 </div>
2741 </div>
2742 </div>
2743
2744 <div class="mxchat-form-group">
2745 <label for="intent_label">
2746 <?php esc_html_e('Action Label (For your reference only)', 'mxchat'); ?>
2747 </label>
2748 <input name="intent_label" type="text" id="intent_label" required
2749 class="mxchat-intent-input"
2750 placeholder="<?php esc_attr_e('Example: Newsletter Signup', 'mxchat'); ?>">
2751 </div>
2752
2753 <div class="mxchat-form-group">
2754 <label for="phrases">
2755 <?php esc_html_e('Trigger Phrases (comma-separated)', 'mxchat'); ?>
2756 </label>
2757 <textarea name="phrases" id="action_phrases" rows="5" required
2758 class="mxchat-intent-textarea"
2759 placeholder="<?php esc_attr_e('Example: sign me up, subscribe me, I want to join, add me to the newsletter', 'mxchat'); ?>"></textarea>
2760 </div>
2761
2762 <div class="mxchat-form-group">
2763 <label for="similarity_threshold">
2764 <?php esc_html_e('Similarity Threshold', 'mxchat'); ?>
2765 <span class="mxchat-threshold-value-display">85%</span>
2766 </label>
2767 <div class="mxchat-slider-group modal-slider">
2768 <input type="range"
2769 name="similarity_threshold"
2770 id="similarity_threshold"
2771 min="70"
2772 max="95"
2773 value="85"
2774 class="mxchat-intent-slider"
2775 oninput="document.querySelector('.mxchat-threshold-value-display').textContent = this.value + '%'">
2776 </div>
2777 <div class="mxchat-threshold-hint">
2778 <?php esc_html_e('Lower values make the action trigger more easily. Higher values require more exact matches.', 'mxchat'); ?>
2779 </div>
2780 </div>
2781
2782 <div class="mxchat-modal-actions">
2783 <button type="button" class="mxchat-button-secondary mxchat-modal-cancel">
2784 <?php esc_html_e('Cancel', 'mxchat'); ?>
2785 </button>
2786 <button type="submit" class="mxchat-button-primary" id="mxchat-save-action-btn">
2787 <?php esc_html_e('Save Action', 'mxchat'); ?>
2788 </button>
2789 </div>
2790 </div>
2791 </form>
2792 </div>
2793 </div>
2794
2795 <div id="mxchat-action-loading" class="mxchat-action-loading" style="display: none;">
2796 <div class="mxchat-action-loading-spinner"></div>
2797 <div class="mxchat-action-loading-text">
2798 <?php esc_html_e('Saving action, please wait...', 'mxchat'); ?>
2799 </div>
2800 </div>
2801 </div><!-- .mxchat-wrapper -->
2802 <?php
2803 }
2804
2805 private function get_trimmed_phrases($phrases, $max_length = 100) {
2806 if (strlen($phrases) <= $max_length) {
2807 return $phrases;
2808 }
2809
2810 $trimmed = substr($phrases, 0, $max_length);
2811 $last_comma = strrpos($trimmed, ',');
2812
2813 if ($last_comma !== false) {
2814 $trimmed = substr($trimmed, 0, $last_comma);
2815 }
2816
2817 return $trimmed . '...';
2818 }
2819
2820 public function mxchat_add_enabled_column_to_intents() {
2821 global $wpdb;
2822 $table_name = $wpdb->prefix . 'mxchat_intents';
2823
2824 // Check if the column already exists
2825 $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'enabled'");
2826
2827 if (empty($columns)) {
2828 // Add the column with default value of 1 (enabled)
2829 $wpdb->query("ALTER TABLE $table_name ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
2830 }
2831 }
2832
2833 /**
2834 * Handle embedding generation errors using existing admin notice system
2835 *
2836 * @param string $message Error message to display
2837 * @param bool $redirect Whether to redirect back to the actions page
2838 * @return void
2839 */
2840 private function handle_embedding_error($message, $redirect = true) {
2841 // Store the error message in the existing transient
2842 set_transient('mxchat_admin_notice_error', $message, 60);
2843
2844 if ($redirect) {
2845 // Redirect back to the actions page
2846 $redirect_url = add_query_arg(
2847 array(
2848 'page' => 'mxchat-actions'
2849 ),
2850 admin_url('admin.php')
2851 );
2852 wp_safe_redirect($redirect_url);
2853 exit;
2854 }
2855 }
2856
2857
2858 /**
2859 * Handle adding new intent - with improved error handling
2860 *
2861 * @return void
2862 */
2863
2864
2865
2866
2867 /**
2868 * Enhanced get_available_callbacks function with form action exclusion
2869 *
2870 * @param bool $grouped Whether to return callbacks grouped by category
2871 * @param bool $include_all Whether to include all potential actions (even if add-on not installed)
2872 * @return array Callbacks data with icons, descriptions and availability status
2873 */
2874 private function mxchat_get_available_callbacks($grouped = false, $include_all = true) {
2875 // Load WordPress plugin functions if needed
2876 if (!function_exists('get_plugins')) {
2877 require_once ABSPATH . 'wp-admin/includes/plugin.php';
2878 }
2879
2880 // Get active plugins
2881 $active_plugins = get_option('active_plugins', array());
2882
2883 // Functions to exclude from the action selector only if Pro is activated
2884 // If user doesn't have Pro, show these so they can see what they're missing
2885 $excluded_when_pro_active_functions = array(
2886 'mxchat_handle_form_collection', // Forms add-on action
2887 'mxchat_sr_recommendation_flow' // Smart Recommender flow actions
2888 );
2889
2890 // Always excluded functions (regardless of Pro status)
2891 $always_excluded_functions = array();
2892
2893 // Combine exclusion lists based on Pro activation status
2894 $excluded_functions = $always_excluded_functions;
2895 if ($this->is_activated) {
2896 // Only exclude add-on managed functions if Pro is active
2897 $excluded_functions = array_merge($excluded_functions, $excluded_when_pro_active_functions);
2898 }
2899
2900 // Define add-on plugin files and their corresponding action functions
2901 $addon_plugins = array(
2902 'mxchat-woo/mxchat-woo.php' => array(
2903 'functions' => array(
2904 'mxchat_handle_product_recommendations',
2905 'mxchat_handle_order_history',
2906 'mxchat_show_product_card',
2907 'mxchat_add_to_cart',
2908 'mxchat_checkout_redirect'
2909 ),
2910 'name' => __('WooCommerce Add-on', 'mxchat'),
2911 'pro_required' => true
2912 ),
2913 'mxchat-perplexity/mxchat-perplexity.php' => array(
2914 'functions' => array('mxchat_perplexity_research'),
2915 'name' => __('Perplexity Add-on', 'mxchat'),
2916 'pro_required' => true
2917 ),
2918 'mxchat-forms/mxchat-forms.php' => array(
2919 'functions' => array('mxchat_handle_form_collection'),
2920 'name' => __('Forms Add-on', 'mxchat'),
2921 'pro_required' => true
2922 ),
2923 'mxchat-smart-recommender/mxchat-smart-recommender.php' => array(
2924 'functions' => array('mxchat_sr_recommendation_flow'),
2925 'name' => __('Smart Recommender Add-on', 'mxchat'),
2926 'pro_required' => true
2927 ),
2928 // Add other add-ons and their functions here
2929 );
2930
2931 // Get the functions that are provided by active add-ons
2932 $addon_provided_functions = array();
2933 $addon_function_mapping = array(); // Maps functions to their add-on info
2934
2935 // Check which add-ons are active
2936 foreach ($addon_plugins as $plugin_file => $addon_info) {
2937 $is_active = in_array($plugin_file, $active_plugins);
2938
2939 // For each function in this addon
2940 foreach ($addon_info['functions'] as $function) {
2941 // Consider a function installed only if:
2942 // 1. The add-on is active AND
2943 // 2. Either it doesn't require Pro OR Pro is activated
2944 $is_installed = $is_active && (!$addon_info['pro_required'] || $this->is_activated);
2945
2946 // If the add-on is installed, mark this function as provided by an add-on
2947 if ($is_installed) {
2948 $addon_provided_functions[] = $function;
2949 }
2950
2951 // Store addon info for this function regardless of installation status
2952 $addon_function_mapping[$function] = array(
2953 'addon' => basename(dirname($plugin_file)),
2954 'addon_name' => $addon_info['name'],
2955 'pro_required' => $addon_info['pro_required'],
2956 'is_active' => $is_active,
2957 'is_installed' => $is_installed
2958 );
2959 }
2960 }
2961
2962 // Core callbacks - always available in the base plugin
2963 $core_callbacks = array(
2964 'mxchat_handle_email_capture' => array(
2965 'label' => __('Loops Email Capture', 'mxchat'),
2966 'pro_only' => false,
2967 'group' => __('Customer Engagement', 'mxchat'),
2968 'icon' => 'email-alt',
2969 'description' => __('Collect visitor emails for your mailing list in Loops', 'mxchat'),
2970 'addon' => false, // Not from an add-on
2971 'installed' => true // Always installed with base plugin
2972 ),
2973 'mxchat_handle_search_request' => array(
2974 'label' => __('Brave Web Search', 'mxchat'),
2975 'pro_only' => false,
2976 'group' => __('Search Features', 'mxchat'),
2977 'icon' => 'search',
2978 'description' => __('Let users search the web directly from the chat', 'mxchat'),
2979 'addon' => false,
2980 'installed' => true
2981 ),
2982 'mxchat_handle_image_search_request' => array(
2983 'label' => __('Brave Image Search', 'mxchat'),
2984 'pro_only' => false,
2985 'group' => __('Search Features', 'mxchat'),
2986 'icon' => 'format-image',
2987 'description' => __('Search and display images in the chat conversation', 'mxchat'),
2988 'addon' => false,
2989 'installed' => true
2990 ),
2991 // Pro core features - check is_activated property
2992 'mxchat_generate_image' => array(
2993 'label' => __('Generate Image', 'mxchat'),
2994 'pro_only' => false,
2995 'group' => __('Other Features', 'mxchat'),
2996 'icon' => 'art',
2997 'description' => __('Create images with DALL-E 3 from OpenAI (requires OpenAI API key)', 'mxchat'),
2998 'addon' => false,
2999 'installed' => true
3000 ),
3001 'mxchat_handle_pdf_discussion' => array(
3002 'label' => __('Chat with PDF', 'mxchat'),
3003 'pro_only' => false,
3004 'group' => __('Other Features', 'mxchat'),
3005 'icon' => 'media-document',
3006 'description' => __('Answer questions about uploaded PDF documents', 'mxchat'),
3007 'addon' => false,
3008 'installed' => true
3009 ),
3010 'mxchat_live_agent_handover' => array(
3011 'label' => __('Slack Live Agent', 'mxchat'),
3012 'pro_only' => false,
3013 'group' => __('Customer Engagement', 'mxchat'),
3014 'icon' => 'admin-users',
3015 'description' => __('Transfer conversation to a human support agent on Slack', 'mxchat'),
3016 'addon' => false,
3017 'installed' => true
3018 ),
3019 'mxchat_handle_switch_to_chatbot_intent' => array(
3020 'label' => __('Back to Chatbot', 'mxchat'),
3021 'pro_only' => false,
3022 'group' => __('Customer Engagement', 'mxchat'),
3023 'icon' => 'backup',
3024 'description' => __('Return from live agent mode to AI chatbot', 'mxchat'),
3025 'addon' => false,
3026 'installed' => true
3027 ),
3028 );
3029
3030 // Add-on callbacks with placeholders - only include if the add-on is NOT active
3031 $addon_callbacks = array(
3032 // WooCommerce Add-on
3033 'mxchat_handle_product_recommendations' => array(
3034 'label' => __('Product Recommendations', 'mxchat'),
3035 'pro_only' => true,
3036 'group' => __('WooCommerce Features', 'mxchat'),
3037 'icon' => 'cart',
3038 'description' => __('Suggest products based on customer preferences', 'mxchat'),
3039 ),
3040 'mxchat_handle_order_history' => array(
3041 'label' => __('Order History', 'mxchat'),
3042 'pro_only' => true,
3043 'group' => __('WooCommerce Features', 'mxchat'),
3044 'icon' => 'clipboard',
3045 'description' => __('Allow customers to check their order status', 'mxchat'),
3046 ),
3047 'mxchat_show_product_card' => array(
3048 'label' => __('Show Product Card', 'mxchat'),
3049 'pro_only' => true,
3050 'group' => __('WooCommerce Features', 'mxchat'),
3051 'icon' => 'products',
3052 'description' => __('Display product information in the chat', 'mxchat'),
3053 ),
3054 'mxchat_add_to_cart' => array(
3055 'label' => __('Add to Cart', 'mxchat'),
3056 'pro_only' => true,
3057 'group' => __('WooCommerce Features', 'mxchat'),
3058 'icon' => 'plus-alt',
3059 'description' => __('Add products to cart directly from chat', 'mxchat'),
3060 ),
3061 'mxchat_checkout_redirect' => array(
3062 'label' => __('Proceed to Checkout', 'mxchat'),
3063 'pro_only' => true,
3064 'group' => __('WooCommerce Features', 'mxchat'),
3065 'icon' => 'arrow-right-alt',
3066 'description' => __('Redirect customer to checkout page', 'mxchat'),
3067 ),
3068
3069 // Perplexity Add-on
3070 'mxchat_perplexity_research' => array(
3071 'label' => __('Perplexity Research', 'mxchat'),
3072 'pro_only' => true,
3073 'group' => __('Search Features', 'mxchat'),
3074 'icon' => 'book-alt',
3075 'description' => __('Allows the chatbot to search the web for accurate, up-to-date answers', 'mxchat'),
3076 ),
3077
3078 // Forms Add-on (only shown when Pro is not activated)
3079 'mxchat_handle_form_collection' => array(
3080 'label' => __('Form Collection', 'mxchat'),
3081 'pro_only' => true,
3082 'group' => __('Form Features', 'mxchat'),
3083 'icon' => 'feedback',
3084 'description' => __('Collect user information through custom forms in chat', 'mxchat'),
3085 ),
3086
3087 // Smart Recommender Add-on (only shown when Pro is not activated)
3088 'mxchat_sr_recommendation_flow' => array(
3089 'label' => __('Smart Recommender Flow', 'mxchat'),
3090 'pro_only' => true,
3091 'group' => __('Recommendation Features', 'mxchat'),
3092 'icon' => 'cart',
3093 'description' => __('Create interactive conversation flows that collect user preferences and deliver personalized product or service recommendations', 'mxchat'),
3094 ),
3095 );
3096
3097 // Enhance add-on callbacks with installation status and addon info
3098 foreach ($addon_callbacks as $function => $data) {
3099 if (isset($addon_function_mapping[$function])) {
3100 $addon_info = $addon_function_mapping[$function];
3101
3102 $addon_callbacks[$function]['addon'] = $addon_info['addon'];
3103 $addon_callbacks[$function]['addon_name'] = $addon_info['addon_name'];
3104 $addon_callbacks[$function]['installed'] = $addon_info['is_installed'];
3105
3106 // Set pro_only based on add-on configuration
3107 $addon_callbacks[$function]['pro_only'] = $addon_info['pro_required'];
3108 } else {
3109 $addon_callbacks[$function]['addon'] = 'unknown';
3110 $addon_callbacks[$function]['addon_name'] = __('Unknown Add-on', 'mxchat');
3111 $addon_callbacks[$function]['installed'] = false;
3112 }
3113 }
3114
3115 // Initialize callbacks with core features
3116 $callbacks = $core_callbacks;
3117
3118 // Get callbacks from active add-ons
3119 $active_addon_callbacks = apply_filters('mxchat_available_callbacks', array());
3120
3121 // Add placeholder callbacks only for add-ons that aren't active
3122 if ($include_all) {
3123 foreach ($addon_callbacks as $function => $data) {
3124 // Skip placeholders for functions provided by active add-ons
3125 if (in_array($function, $addon_provided_functions)) {
3126 continue;
3127 }
3128
3129 // Skip excluded functions
3130 if (in_array($function, $excluded_functions)) {
3131 continue;
3132 }
3133
3134 // Add the placeholder
3135 $callbacks[$function] = $data;
3136 }
3137 }
3138
3139 // Add callbacks from active add-ons (will override placeholders)
3140 foreach ($active_addon_callbacks as $function => $data) {
3141 // Skip excluded functions
3142 if (in_array($function, $excluded_functions)) {
3143 continue;
3144 }
3145
3146 // Always include callbacks from add-ons
3147 $callbacks[$function] = $data;
3148
3149 // Ensure they have the proper add-on info
3150 if (isset($addon_function_mapping[$function])) {
3151 $addon_info = $addon_function_mapping[$function];
3152 $callbacks[$function]['addon'] = $addon_info['addon'];
3153 $callbacks[$function]['addon_name'] = $addon_info['addon_name'];
3154 $callbacks[$function]['installed'] = $addon_info['is_installed'];
3155 $callbacks[$function]['pro_only'] = $addon_info['pro_required'];
3156 }
3157 }
3158
3159 // Just before returning callbacks, sort them to prioritize free features
3160 if (!$grouped) {
3161 // Create temporary arrays for sorting
3162 $free_callbacks = array();
3163 $pro_callbacks = array();
3164
3165 // Split callbacks into free and pro
3166 foreach ($callbacks as $key => $data) {
3167 if (isset($data['pro_only']) && $data['pro_only']) {
3168 $pro_callbacks[$key] = $data;
3169 } else {
3170 $free_callbacks[$key] = $data;
3171 }
3172 }
3173
3174 // Merge with free callbacks first
3175 $callbacks = array_merge($free_callbacks, $pro_callbacks);
3176 }
3177
3178 // Return grouped structure if requested
3179 if ($grouped) {
3180 $grouped_callbacks = array();
3181 foreach ($callbacks as $key => $data) {
3182 $group_label = isset($data['group']) ? $data['group'] : __('Other Features', 'mxchat');
3183
3184 // Ensure we carry forward all the new fields in grouped mode
3185 $callback_data = array(
3186 'label' => $data['label'],
3187 'pro_only' => isset($data['pro_only']) ? $data['pro_only'] : false,
3188 'icon' => isset($data['icon']) ? $data['icon'] : 'admin-generic',
3189 'description' => isset($data['description']) ? $data['description'] : __('Custom action for your chatbot', 'mxchat'),
3190 'addon' => isset($data['addon']) ? $data['addon'] : false,
3191 'addon_name' => isset($data['addon_name']) ? $data['addon_name'] : '',
3192 'installed' => isset($data['installed']) ? $data['installed'] : true
3193 );
3194
3195 $grouped_callbacks[$group_label][$key] = $callback_data;
3196 }
3197
3198 // Sort within each group to prioritize free features
3199 foreach ($grouped_callbacks as $group => $items) {
3200 $free_items = array();
3201 $pro_items = array();
3202
3203 foreach ($items as $key => $data) {
3204 if (isset($data['pro_only']) && $data['pro_only']) {
3205 $pro_items[$key] = $data;
3206 } else {
3207 $free_items[$key] = $data;
3208 }
3209 }
3210
3211 $grouped_callbacks[$group] = array_merge($free_items, $pro_items);
3212 }
3213
3214 return $grouped_callbacks;
3215 }
3216
3217 return $callbacks;
3218 }
3219
3220 public function mxchat_handle_delete_intent() {
3221 if ( ! current_user_can( 'manage_options' ) ) {
3222 wp_die( esc_html__('Unauthorized user', 'mxchat') );
3223 }
3224
3225 check_admin_referer('mxchat_delete_intent_nonce');
3226
3227 if (isset($_POST['intent_id'])) {
3228 global $wpdb;
3229 $table_name = $wpdb->prefix . 'mxchat_intents';
3230 $intent_id = intval($_POST['intent_id']);
3231
3232 $wpdb->delete($table_name, ['id' => $intent_id], ['%d']);
3233 }
3234
3235 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
3236 exit;
3237 }
3238
3239
3240 public function mxchat_page_init() {
3241 register_setting(
3242 'mxchat_option_group',
3243 'mxchat_options',
3244 array($this, 'mxchat_sanitize')
3245 );
3246
3247 register_setting(
3248 'mxchat_option_group',
3249 'mxchat_similarity_threshold',
3250 array(
3251 'type' => 'number',
3252 'sanitize_callback' => function($value) {
3253 $value = absint($value);
3254 return min(max($value, 20), 95);
3255 },
3256 'default' => 80,
3257 )
3258 );
3259
3260 // Chatbot Settings Section
3261 add_settings_section(
3262 'mxchat_chatbot_section',
3263 esc_html__('Chatbot Settings', 'mxchat'),
3264 null,
3265 'mxchat-chatbot'
3266 );
3267
3268 // Similarity Threshold Slider
3269 add_settings_field(
3270 'similarity_threshold', // Field ID
3271 esc_html__('Similarity Threshold', 'mxchat'), // Field title
3272 array($this, 'mxchat_similarity_threshold_callback'), // Callback function
3273 'mxchat-chatbot', // Page
3274 'mxchat_chatbot_section' // Section
3275 );
3276
3277 add_settings_field(
3278 'append_to_body',
3279 esc_html__('Auto-Display Chatbot', 'mxchat'),
3280 array($this, 'mxchat_append_to_body_callback'),
3281 'mxchat-chatbot',
3282 'mxchat_chatbot_section'
3283 );
3284
3285 add_settings_field(
3286 'api_key',
3287 esc_html__('OpenAI API Key', 'mxchat'),
3288 array($this, 'api_key_callback'),
3289 'mxchat-chatbot',
3290 'mxchat_chatbot_section',
3291 array(
3292 'class' => 'mxchat-setting-row',
3293 'data-provider' => 'openai'
3294 )
3295 );
3296
3297 add_settings_field(
3298 'xai_api_key',
3299 esc_html__('X.AI API Key', 'mxchat'),
3300 array($this, 'xai_api_key_callback'),
3301 'mxchat-chatbot',
3302 'mxchat_chatbot_section',
3303 array(
3304 'class' => 'mxchat-setting-row',
3305 'data-provider' => 'xai'
3306 )
3307 );
3308
3309 add_settings_field(
3310 'claude_api_key',
3311 esc_html__('Claude API Key', 'mxchat'),
3312 array($this, 'claude_api_key_callback'),
3313 'mxchat-chatbot',
3314 'mxchat_chatbot_section',
3315 array(
3316 'class' => 'mxchat-setting-row',
3317 'data-provider' => 'claude'
3318 )
3319 );
3320
3321 add_settings_field(
3322 'deepseek_api_key',
3323 esc_html__('DeepSeek API Key', 'mxchat'),
3324 array($this, 'deepseek_api_key_callback'),
3325 'mxchat-chatbot',
3326 'mxchat_chatbot_section',
3327 array(
3328 'class' => 'mxchat-setting-row',
3329 'data-provider' => 'deepseek'
3330 )
3331 );
3332
3333 add_settings_field(
3334 'gemini_api_key',
3335 esc_html__('Google Gemini API Key', 'mxchat'),
3336 array($this, 'gemini_api_key_callback'),
3337 'mxchat-chatbot',
3338 'mxchat_chatbot_section',
3339 array(
3340 'class' => 'mxchat-setting-row',
3341 'data-provider' => 'gemini'
3342 )
3343 );
3344
3345 add_settings_field(
3346 'voyage_api_key',
3347 esc_html__('Voyage AI API Key', 'mxchat'),
3348 array($this, 'voyage_api_key_callback'),
3349 'mxchat-chatbot',
3350 'mxchat_chatbot_section',
3351 array(
3352 'class' => 'mxchat-setting-row',
3353 'data-provider' => 'voyage'
3354 )
3355 );
3356
3357 add_settings_field(
3358 'enable_streaming_toggle',
3359 esc_html__('Enable Streaming', 'mxchat'),
3360 array($this, 'enable_streaming_toggle_callback'),
3361 'mxchat-chatbot',
3362 'mxchat_chatbot_section', // Same section as your working toggle
3363 array(
3364 'class' => 'mxchat-setting-row streaming-setting',
3365 'style' => 'display: none;' // Hidden by default, shown when OpenAI/Claude selected
3366 )
3367 );
3368
3369
3370 add_settings_field(
3371 'model',
3372 esc_html__('Chat Model', 'mxchat'),
3373 array($this, 'mxchat_model_callback'),
3374 'mxchat-chatbot',
3375 'mxchat_chatbot_section'
3376 );
3377
3378 add_settings_field(
3379 'embedding_model',
3380 esc_html__('Embedding Model', 'mxchat'),
3381 array($this, 'embedding_model_callback'),
3382 'mxchat-chatbot',
3383 'mxchat_chatbot_section'
3384 );
3385
3386 add_settings_field(
3387 'system_prompt_instructions',
3388 esc_html__('AI Instructions (Behavior)', 'mxchat'),
3389 array($this, 'system_prompt_instructions_callback'),
3390 'mxchat-chatbot',
3391 'mxchat_chatbot_section'
3392 );
3393
3394
3395 add_settings_field(
3396 'top_bar_title',
3397 esc_html__('Top Bar Title', 'mxchat'),
3398 array($this, 'mxchat_top_bar_title_callback'),
3399 'mxchat-chatbot',
3400 'mxchat_chatbot_section'
3401 );
3402
3403 add_settings_field(
3404 'ai_agent_text',
3405 esc_html__('AI Agent Text', 'mxchat'),
3406 array($this, 'mxchat_ai_agent_text_callback'),
3407 'mxchat-chatbot',
3408 'mxchat_chatbot_section'
3409 );
3410
3411 add_settings_field(
3412 'enable_email_block',
3413 esc_html__('Require Email To Chat', 'mxchat'),
3414 array($this, 'enable_email_block_callback'),
3415 'mxchat-chatbot',
3416 'mxchat_chatbot_section'
3417 );
3418
3419 add_settings_field(
3420 'email_blocker_header_content',
3421 esc_html__('Require Email Chat Content', 'mxchat'),
3422 array($this, 'email_blocker_header_content_callback'),
3423 'mxchat-chatbot',
3424 'mxchat_chatbot_section'
3425 );
3426
3427 add_settings_field(
3428 'email_blocker_button_text',
3429 esc_html__('Require Email Chat Button Text', 'mxchat'),
3430 [$this, 'email_blocker_button_text_callback'],
3431 'mxchat-chatbot',
3432 'mxchat_chatbot_section'
3433 );
3434
3435 add_settings_field(
3436 'intro_message',
3437 esc_html__('Introductory Message', 'mxchat'),
3438 array($this, 'mxchat_intro_message_callback'),
3439 'mxchat-chatbot',
3440 'mxchat_chatbot_section'
3441 );
3442
3443 add_settings_field(
3444 'input_copy',
3445 esc_html__('Input Copy', 'mxchat'),
3446 array($this, 'mxchat_input_copy_callback'),
3447 'mxchat-chatbot',
3448 'mxchat_chatbot_section'
3449 );
3450
3451 add_settings_field(
3452 'pre_chat_message',
3453 esc_html__('Chat Teaser Pop-up', 'mxchat'),
3454 array($this, 'mxchat_pre_chat_message_callback'),
3455 'mxchat-chatbot',
3456 'mxchat_chatbot_section'
3457 );
3458
3459 add_settings_field(
3460 'privacy_toggle',
3461 esc_html__('Toggle Privacy Notice', 'mxchat'),
3462 array($this, 'mxchat_privacy_toggle_callback'),
3463 'mxchat-chatbot',
3464 'mxchat_chatbot_section'
3465 );
3466
3467 add_settings_field(
3468 'complianz_toggle',
3469 esc_html__('Enable Complianz', 'mxchat'),
3470 array($this, 'mxchat_complianz_toggle_callback'),
3471 'mxchat-chatbot',
3472 'mxchat_chatbot_section'
3473 );
3474
3475 add_settings_field(
3476 'link_target_toggle',
3477 esc_html__('Open Links in a New Tab', 'mxchat'),
3478 array($this, 'mxchat_link_target_toggle_callback'),
3479 'mxchat-chatbot',
3480 'mxchat_chatbot_section'
3481 );
3482
3483 add_settings_field(
3484 'chat_persistence_toggle',
3485 esc_html__('Enable Chat Persistence', 'mxchat'),
3486 array($this, 'mxchat_chat_persistence_toggle_callback'),
3487 'mxchat-chatbot',
3488 'mxchat_chatbot_section'
3489 );
3490
3491 add_settings_field(
3492 'popular_question_1',
3493 esc_html__('Quick Question 1', 'mxchat'),
3494 array($this, 'mxchat_popular_question_1_callback'),
3495 'mxchat-chatbot',
3496 'mxchat_chatbot_section'
3497 );
3498
3499 add_settings_field(
3500 'popular_question_2',
3501 esc_html__('Quick Question 2', 'mxchat'),
3502 array($this, 'mxchat_popular_question_2_callback'),
3503 'mxchat-chatbot',
3504 'mxchat_chatbot_section'
3505 );
3506
3507 add_settings_field(
3508 'popular_question_3',
3509 esc_html__('Quick Question 3', 'mxchat'),
3510 array($this, 'mxchat_popular_question_3_callback'),
3511 'mxchat-chatbot',
3512 'mxchat_chatbot_section'
3513 );
3514
3515 add_settings_field(
3516 'additional_popular_questions',
3517 esc_html__('Additional Quick Questions', 'mxchat'),
3518 array($this, 'mxchat_additional_popular_questions_callback'),
3519 'mxchat-chatbot',
3520 'mxchat_chatbot_section'
3521 );
3522
3523
3524 add_settings_field(
3525 'rate_limits',
3526 __('Rate Limits Settings', 'mxchat'),
3527 array($this, 'mxchat_rate_limits_callback'),
3528 'mxchat-chatbot',
3529 'mxchat_chatbot_section'
3530 );
3531
3532 // Loops Settings Section
3533 add_settings_section(
3534 'mxchat_loops_section',
3535 esc_html__('Loops Settings', 'mxchat'),
3536 null,
3537 'mxchat-embed'
3538 );
3539
3540 // Loops Settings Fields
3541 add_settings_field(
3542 'loops_api_key',
3543 esc_html__('Loops API Key', 'mxchat'),
3544 array($this, 'mxchat_loops_api_key_callback'),
3545 'mxchat-embed',
3546 'mxchat_loops_section'
3547 );
3548
3549 add_settings_field(
3550 'loops_mailing_list',
3551 esc_html__('Loops Mailing List', 'mxchat'),
3552 array($this, 'mxchat_loops_mailing_list_callback'),
3553 'mxchat-embed',
3554 'mxchat_loops_section'
3555 );
3556
3557 add_settings_field(
3558 'triggered_phrase_response',
3559 esc_html__('Triggered Phrase Response', 'mxchat'),
3560 array($this, 'mxchat_triggered_phrase_response_callback'),
3561 'mxchat-embed',
3562 'mxchat_loops_section'
3563 );
3564
3565 add_settings_field(
3566 'email_capture_response',
3567 esc_html__('Email Capture Response', 'mxchat'),
3568 array($this, 'mxchat_email_capture_response_callback'),
3569 'mxchat-embed',
3570 'mxchat_loops_section'
3571 );
3572
3573 // Brave Search Settings Fields
3574 add_settings_section(
3575 'mxchat_brave_section',
3576 __('Brave Search Settings', 'mxchat'),
3577 array($this, 'mxchat_brave_section_callback'),
3578 'mxchat-embed'
3579 );
3580
3581 add_settings_field(
3582 'brave_api_key',
3583 __('Brave API Key', 'mxchat'),
3584 array($this, 'mxchat_brave_api_key_callback'),
3585 'mxchat-embed',
3586 'mxchat_brave_section'
3587 );
3588
3589 add_settings_field(
3590 'brave_image_count',
3591 __('Number of Images to Return', 'mxchat'),
3592 array($this, 'mxchat_brave_image_count_callback'),
3593 'mxchat-embed',
3594 'mxchat_brave_section'
3595 );
3596
3597 add_settings_field(
3598 'brave_safe_search',
3599 __('Safe Search', 'mxchat'),
3600 array($this, 'mxchat_brave_safe_search_callback'),
3601 'mxchat-embed',
3602 'mxchat_brave_section'
3603 );
3604
3605 add_settings_field(
3606 'brave_news_count',
3607 __('Number of News Articles', 'mxchat'),
3608 array($this, 'mxchat_brave_news_count_callback'),
3609 'mxchat-embed',
3610 'mxchat_brave_section'
3611 );
3612
3613 add_settings_field(
3614 'brave_country',
3615 __('Country', 'mxchat'),
3616 array($this, 'mxchat_brave_country_callback'),
3617 'mxchat-embed',
3618 'mxchat_brave_section'
3619 );
3620
3621 add_settings_field(
3622 'brave_language',
3623 __('Language', 'mxchat'),
3624 array($this, 'mxchat_brave_language_callback'),
3625 'mxchat-embed',
3626 'mxchat_brave_section'
3627 );
3628
3629 // Chat with PDF Intent Settings Fields
3630 add_settings_section(
3631 'mxchat_pdf_intent_section',
3632 __('Toolbar Settings & Intents', 'mxchat'),
3633 array($this, 'mxchat_pdf_intent_section_callback'),
3634 'mxchat-embed'
3635 );
3636
3637 add_settings_field(
3638 'chat_toolbar_toggle',
3639 __('Show Chat Toolbar', 'mxchat'),
3640 array($this, 'mxchat_chat_toolbar_toggle_callback'),
3641 'mxchat-embed',
3642 'mxchat_pdf_intent_section'
3643 );
3644
3645 // PDF Upload Button Toggle
3646 add_settings_field(
3647 'show_pdf_upload_button',
3648 __('Show PDF Upload Button', 'mxchat'),
3649 array($this, 'mxchat_show_pdf_upload_button_callback'),
3650 'mxchat-embed',
3651 'mxchat_pdf_intent_section'
3652 );
3653
3654 // Word Upload Button Toggle
3655 add_settings_field(
3656 'show_word_upload_button',
3657 __('Show Word Upload Button', 'mxchat'),
3658 array($this, 'mxchat_show_word_upload_button_callback'),
3659 'mxchat-embed',
3660 'mxchat_pdf_intent_section'
3661 );
3662
3663 add_settings_field(
3664 'pdf_intent_trigger_text',
3665 __('Intent Trigger Text', 'mxchat'),
3666 array($this, 'mxchat_pdf_intent_trigger_text_callback'),
3667 'mxchat-embed',
3668 'mxchat_pdf_intent_section'
3669 );
3670
3671 add_settings_field(
3672 'pdf_intent_success_text',
3673 __('Success Text', 'mxchat'),
3674 array($this, 'mxchat_pdf_intent_success_text_callback'),
3675 'mxchat-embed',
3676 'mxchat_pdf_intent_section'
3677 );
3678
3679 add_settings_field(
3680 'pdf_intent_error_text',
3681 __('Error Text', 'mxchat'),
3682 array($this, 'mxchat_pdf_intent_error_text_callback'),
3683 'mxchat-embed',
3684 'mxchat_pdf_intent_section'
3685 );
3686
3687 // Add PDF Maximum Pages Field
3688 add_settings_field(
3689 'pdf_max_pages',
3690 __('Maximum Document Pages', 'mxchat'),
3691 array($this, 'mxchat_pdf_max_pages_callback'),
3692 'mxchat-embed',
3693 'mxchat_pdf_intent_section'
3694 );
3695
3696 // Live Agent Settings Fields
3697 add_settings_section(
3698 'mxchat_live_agent_section',
3699 __('Live Agent Settings', 'mxchat'),
3700 array($this, 'mxchat_live_agent_section_callback'),
3701 'mxchat-embed'
3702 );
3703
3704 // Live Agent Status Fields (add at top of live agent settings)
3705 add_settings_field(
3706 'live_agent_status',
3707 __('Live Agent Status', 'mxchat'),
3708 array($this, 'mxchat_live_agent_status_callback'),
3709 'mxchat-embed',
3710 'mxchat_live_agent_section'
3711 );
3712
3713 add_settings_field(
3714 'live_agent_notification_message',
3715 __('Notification Message', 'mxchat'),
3716 array($this, 'mxchat_live_agent_notification_message_callback'),
3717 'mxchat-embed',
3718 'mxchat_live_agent_section'
3719 );
3720
3721 add_settings_field(
3722 'live_agent_away_message',
3723 __('Away Message', 'mxchat'),
3724 array($this, 'mxchat_live_agent_away_message_callback'),
3725 'mxchat-embed',
3726 'mxchat_live_agent_section'
3727 );
3728
3729 add_settings_field(
3730 'live_agent_user_ids',
3731 __('Slack Agent User IDs', 'mxchat'),
3732 array($this, 'mxchat_live_agent_user_ids_callback'),
3733 'mxchat-embed',
3734 'mxchat_live_agent_section'
3735 );
3736
3737 add_settings_field(
3738 'live_agent_webhook_url',
3739 __('Slack Webhook URL', 'mxchat'),
3740 array($this, 'mxchat_live_agent_webhook_url_callback'),
3741 'mxchat-embed',
3742 'mxchat_live_agent_section'
3743 );
3744
3745 add_settings_field(
3746 'live_agent_secret_key',
3747 __('Slack Secret Key', 'mxchat'),
3748 array($this, 'mxchat_live_agent_secret_key_callback'),
3749 'mxchat-embed',
3750 'mxchat_live_agent_section'
3751 );
3752
3753 // Live Agent Integration Fields
3754 add_settings_field(
3755 'live_agent_bot_token',
3756 __('Slack Bot OAuth Token', 'mxchat'),
3757 array($this, 'mxchat_live_agent_bot_token_callback'),
3758 'mxchat-embed',
3759 'mxchat_live_agent_section'
3760 );
3761
3762
3763
3764
3765 // General Settings Section
3766 add_settings_section(
3767 'mxchat_general_section',
3768 esc_html__('YouTube Tutorials', 'mxchat'),
3769 null,
3770 'mxchat-general'
3771 );
3772 }
3773
3774 public function mxchat_prompts_page_init() {
3775 register_setting(
3776 'mxchat_prompts_options',
3777 'mxchat_prompts_options',
3778 array(
3779 'type' => 'array',
3780 'description' => __('MXChat Knowledge Base Settings', 'mxchat'),
3781 'default' => array(
3782 'mxchat_auto_sync_posts' => 0,
3783 'mxchat_auto_sync_pages' => 0,
3784 'mxchat_use_pinecone' => 0,
3785 'mxchat_pinecone_api_key' => '',
3786 'mxchat_pinecone_environment' => '',
3787 'mxchat_pinecone_index' => '',
3788 'mxchat_pinecone_host' => '',
3789 ),
3790 'sanitize_callback' => array($this, 'sanitize_prompts_options'),
3791 )
3792 );
3793
3794 add_action('admin_notices', array($this, 'sync_settings_notice'));
3795 }
3796
3797 public function mxchat_transcripts_page_init() {
3798 register_setting(
3799 'mxchat_transcripts_options',
3800 'mxchat_transcripts_options',
3801 array(
3802 'type' => 'array',
3803 'description' => __('MXChat Transcripts Notification Settings', 'mxchat'),
3804 'default' => array(
3805 'mxchat_enable_notifications' => 0,
3806 'mxchat_notification_email' => get_option('admin_email'),
3807 ),
3808 'sanitize_callback' => array($this, 'sanitize_transcripts_options'),
3809 )
3810 );
3811
3812 add_settings_section(
3813 'mxchat_transcripts_notification_section',
3814 esc_html__('Chat Notification Settings', 'mxchat'),
3815 array($this, 'mxchat_transcripts_notification_section_callback'),
3816 'mxchat-transcripts'
3817 );
3818
3819 add_settings_field(
3820 'mxchat_enable_notifications',
3821 esc_html__('Enable Chat Notifications', 'mxchat'),
3822 array($this, 'mxchat_enable_notifications_callback'),
3823 'mxchat-transcripts',
3824 'mxchat_transcripts_notification_section'
3825 );
3826
3827 add_settings_field(
3828 'mxchat_notification_email',
3829 esc_html__('Notification Email Address', 'mxchat'),
3830 array($this, 'mxchat_notification_email_callback'),
3831 'mxchat-transcripts',
3832 'mxchat_transcripts_notification_section'
3833 );
3834 }
3835
3836
3837 /**
3838 * Sanitize all prompts options
3839 *
3840 * @param array $input The unsanitized options array
3841 * @return array The sanitized options array
3842 */
3843 public function sanitize_prompts_options($input) {
3844 // Log the incoming input.
3845 //error_log('Sanitizing inputs: ' . print_r($input, true));
3846
3847 $sanitized = array();
3848
3849 // Boolean options
3850 $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0;
3851 $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0;
3852 $sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0;
3853
3854 // API Key: if less than 32 characters, flag as invalid.
3855 $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
3856 if (!empty($api_key) && strlen($api_key) < 32) {
3857 add_settings_error(
3858 'mxchat_prompts_options',
3859 'invalid_api_key',
3860 __('The Pinecone API key appears to be invalid. Please check your API key.', 'mxchat')
3861 );
3862 $existing_options = get_option('mxchat_prompts_options', array());
3863 $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? '';
3864 } else {
3865 $sanitized['mxchat_pinecone_api_key'] = $api_key;
3866 }
3867
3868 // Environment and Index Name
3869 $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
3870 $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
3871
3872 // Host: Remove protocol and validate format.
3873 $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
3874 $host = preg_replace('#^https?://#', '', $host);
3875 //error_log('Host after removing protocol: ' . $host);
3876 if (!empty($host)) {
3877 if (!preg_match('/^[\w-]+\.svc\.[\w-]+\.pinecone\.io$/', $host)) {
3878 add_settings_error(
3879 'mxchat_prompts_options',
3880 'invalid_host',
3881 __('The Pinecone host appears to be invalid. It should look like "mxchat-vectors-zrmsquq.svc.aped-4627-b74a.pinecone.io"', 'mxchat')
3882 );
3883 $existing_options = get_option('mxchat_prompts_options', array());
3884 $sanitized['mxchat_pinecone_host'] = $existing_options['mxchat_pinecone_host'] ?? '';
3885 } else {
3886 $sanitized['mxchat_pinecone_host'] = $host;
3887 }
3888 } else {
3889 $sanitized['mxchat_pinecone_host'] = '';
3890 }
3891
3892 //error_log('Final sanitized array: ' . print_r($sanitized, true));
3893
3894 return $sanitized;
3895 }
3896
3897
3898 public function sync_settings_notice() {
3899 // Only show notice on our plugin page
3900 if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') {
3901 return;
3902 }
3903
3904 // Check if settings were updated
3905 if (isset($_GET['settings-updated'])) {
3906
3907 ?>
3908 <div class="notice notice-success is-dismissible">
3909 <p><?php esc_html_e('Sync settings updated successfully.', 'mxchat'); ?></p>
3910 </div>
3911 <?php
3912
3913 }
3914 }
3915 // Add this sanitization function to your class
3916 public function sanitize_sync_setting($input) {
3917 return (bool)$input ? __('1', 'mxchat') : __('', 'mxchat');
3918 }
3919
3920 public function mxchat_rate_limits_callback() {
3921 $all_options = get_option('mxchat_options', []);
3922
3923 // Define available rate limits
3924 $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
3925
3926 // Define available timeframes
3927 $timeframes = array(
3928 'hourly' => __('Per Hour', 'mxchat'),
3929 'daily' => __('Per Day', 'mxchat'),
3930 'weekly' => __('Per Week', 'mxchat'),
3931 'monthly' => __('Per Month', 'mxchat')
3932 );
3933
3934 // Get all roles plus a "logged_out" pseudo-role
3935 $roles = wp_roles()->get_names();
3936 $roles['logged_out'] = __('Logged Out Users', 'mxchat');
3937
3938 // Start the wrapper
3939 echo '<div class="pro-feature-wrapper active">';
3940 echo '<div class="mxchat-rate-limits-container">';
3941
3942 echo '<p class="description" style="margin-bottom: 20px;">' .
3943 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') .
3944 '</p>';
3945
3946 // Add markdown link documentation
3947 echo '<div class="notice notice-info inline" style="margin-bottom: 20px; padding: 10px;">';
3948 echo '<p><strong>' . esc_html__('Markdown Links Supported:', 'mxchat') . '</strong></p>';
3949 echo '<p>' . esc_html__('You can include clickable links in your custom messages using markdown syntax:', 'mxchat') . '</p>';
3950 echo '<ul style="margin-left: 20px;">';
3951 echo '<li><code>[Link text](https://example.com)</code> - Creates a clickable link</li>';
3952 echo '<li><code>[Visit our pricing](https://example.com/pricing)</code> - Link with custom text</li>';
3953 echo '<li><code>Plain URLs like https://example.com will also become clickable</code></li>';
3954 echo '</ul>';
3955 echo '</div>';
3956
3957 // Output the controls for each role
3958 foreach ($roles as $role_id => $role_name) {
3959 // Get saved options or defaults
3960 $default_limit = ($role_id === 'logged_out') ? '10' : '100';
3961 $default_timeframe = 'daily';
3962 $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
3963
3964 $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
3965 ? $all_options['rate_limits'][$role_id]['limit']
3966 : $default_limit;
3967
3968 $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
3969 ? $all_options['rate_limits'][$role_id]['timeframe']
3970 : $default_timeframe;
3971
3972 $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
3973 ? $all_options['rate_limits'][$role_id]['message']
3974 : $default_message;
3975
3976 // Output the row
3977 echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
3978
3979 // Role label
3980 echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
3981
3982 // Controls section
3983 echo '<div class="mxchat-rate-limit-controls-wrapper">';
3984
3985 // Rate limit and timeframe controls
3986 echo '<div class="mxchat-rate-limit-controls">';
3987
3988 // Limit dropdown
3989 echo '<div>';
3990 echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
3991 echo '<select
3992 id="rate_limits_' . esc_attr($role_id) . '_limit"
3993 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
3994 class="mxchat-autosave-field">';
3995 foreach ($rate_limits as $limit) {
3996 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3997 }
3998 echo '</select>';
3999 echo '</div>';
4000
4001 // Timeframe dropdown
4002 echo '<div>';
4003 echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
4004 echo '<select
4005 id="rate_limits_' . esc_attr($role_id) . '_timeframe"
4006 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
4007 class="mxchat-autosave-field">';
4008 foreach ($timeframes as $value => $label) {
4009 echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
4010 }
4011 echo '</select>';
4012 echo '</div>';
4013
4014 echo '</div>'; // End controls
4015
4016 // Custom message textarea
4017 echo '<div class="mxchat-rate-limit-message">';
4018 echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
4019 echo '<textarea
4020 id="rate_limits_' . esc_attr($role_id) . '_message"
4021 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
4022 class="mxchat-autosave-field"
4023 placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
4024 esc_textarea($custom_message) .
4025 '</textarea>';
4026 echo '<p class="description">' .
4027 esc_html__('Example: Rate limit reached! [Visit our pricing page](https://example.com/pricing) to upgrade.', 'mxchat') .
4028 '</p>';
4029 echo '</div>'; // End message
4030
4031 echo '</div>'; // End controls wrapper
4032
4033 echo '</div>'; // End row
4034 }
4035
4036 echo '</div>'; // End container
4037
4038 echo '</div>'; // End pro-feature-wrapper
4039 }
4040
4041
4042 private function mxchat_add_option_field($id, $title, $callback = '') {
4043 add_settings_field(
4044 $id,
4045 __($title, 'mxchat'),
4046 $callback ? array($this, $callback) : array($this, $id . '_callback'),
4047 'mxchat-max',
4048 'mxchat_setting_section_id',
4049 $id === 'model' ? ['label_for' => 'model'] : []
4050 );
4051 }
4052
4053 // OpenAI API Key
4054 public function api_key_callback() {
4055 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
4056
4057 echo '<div class="api-key-wrapper" data-provider="openai">';
4058 echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4059 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4060 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4061 echo '</div>';
4062 }
4063
4064 // X.AI API Key
4065 public function xai_api_key_callback() {
4066 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
4067
4068 echo '<div class="api-key-wrapper" data-provider="xai">';
4069 echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
4070 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4071 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4072 echo '</div>';
4073 }
4074 // Claude API Key
4075 public function claude_api_key_callback() {
4076 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
4077
4078 echo '<div class="api-key-wrapper" data-provider="claude">';
4079 echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
4080 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4081 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4082 echo '</div>';
4083 }
4084
4085 // DeepSeek API Key
4086 public function deepseek_api_key_callback() {
4087 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4088
4089 echo '<div class="api-key-wrapper" data-provider="deepseek">';
4090 echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4091 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4092 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4093 echo '</div>';
4094 }
4095
4096 // Gemini API Key
4097 public function gemini_api_key_callback() {
4098 $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
4099
4100 echo '<div class="api-key-wrapper" data-provider="gemini">';
4101 echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
4102 echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4103 echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
4104 echo '</div>';
4105 }
4106
4107 // Voyage API Key
4108 public function voyage_api_key_callback() {
4109 $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4110
4111 echo '<div class="api-key-wrapper" data-provider="voyage">';
4112 echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4113 echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4114 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
4115 echo '</div>';
4116 }
4117
4118 public function mxchat_loops_api_key_callback() {
4119 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4120
4121 // Hidden fields to "trap" autofill
4122 echo '<input type="text" style="display:none" autocomplete="username" />';
4123 echo '<input type="password" style="display:none" autocomplete="current-password" />';
4124
4125 echo '<div class="api-key-wrapper" data-provider="loops">';
4126 echo sprintf(
4127 '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
4128 $loops_api_key
4129 );
4130 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4131 echo '</div>';
4132 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>';
4133 }
4134 public function mxchat_loops_mailing_list_callback() {
4135 // Add error handling and type checking
4136 $loops_api_key = '';
4137 $selected_list = '';
4138
4139 // Safely get the API key
4140 if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
4141 $loops_api_key = $this->options['loops_api_key'];
4142 }
4143
4144 // Safely get the selected list
4145 if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
4146 $selected_list = $this->options['loops_mailing_list'];
4147 }
4148
4149 if (!empty($loops_api_key)) {
4150 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4151 if (is_array($lists) && !empty($lists)) {
4152 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
4153
4154 // Add a default "Select a list" option
4155 echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
4156
4157 foreach ($lists as $list) {
4158 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4159 echo sprintf(
4160 '<option value="%s" %s>%s</option>',
4161 esc_attr($list['id']),
4162 selected($selected_list, $list['id'], false),
4163 esc_html($list['name'])
4164 );
4165 }
4166 }
4167 echo '</select>';
4168 echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
4169 } else {
4170 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4171 }
4172 } else {
4173 echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
4174 }
4175 }
4176 public function mxchat_triggered_phrase_response_callback() {
4177 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4178 $triggered_response = isset($this->options['triggered_phrase_response'])
4179 ? $this->options['triggered_phrase_response']
4180 : $default_response;
4181
4182 echo sprintf(
4183 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4184 esc_textarea($triggered_response)
4185 );
4186 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>';
4187 }
4188
4189 public function mxchat_email_capture_response_callback() {
4190 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4191 $email_capture_response = isset($this->options['email_capture_response'])
4192 ? $this->options['email_capture_response']
4193 : $default_response;
4194
4195 echo sprintf(
4196 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4197 esc_textarea($email_capture_response)
4198 );
4199 echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
4200 }
4201
4202 public function mxchat_pre_chat_message_callback() {
4203 // Load the entire 'mxchat_options' array
4204 $all_options = get_option('mxchat_options', []);
4205
4206 // Retrieve the saved message or use the default value
4207 $default_message = __('Hey there! Ask me anything!', 'mxchat');
4208 $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
4209
4210 // Output the textarea
4211 printf(
4212 '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
4213 esc_textarea($pre_chat_message)
4214 );
4215 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>';
4216 }
4217
4218 // Callback for AI Instructions textarea
4219 public function system_prompt_instructions_callback() {
4220 // Retrieve the current value of the system prompt instructions
4221 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4222 // Render the textarea field
4223 printf(
4224 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4225 $instructions
4226 );
4227 // Provide a helpful description with sample instructions button
4228 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>';
4229 echo '<div class="mxchat-instructions-container">';
4230 echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
4231 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">';
4232 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4233 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4234 echo '</svg>';
4235 echo esc_html__('View Sample Instructions', 'mxchat');
4236 echo '</button>';
4237 echo '</div>';
4238
4239 // Add modal to WordPress admin footer instead of inline
4240 add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
4241 }
4242
4243 // New method to render modal in admin footer
4244 public function render_sample_instructions_modal() {
4245 static $modal_rendered = false;
4246 if ($modal_rendered) return; // Prevent duplicate modals
4247 $modal_rendered = true;
4248
4249 echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
4250 echo '<div class="mxchat-instructions-modal-content">';
4251 echo '<div class="mxchat-instructions-modal-header">';
4252 echo '<h3 class="mxchat-instructions-modal-title">';
4253 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">';
4254 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4255 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4256 echo '</svg>';
4257 echo esc_html__('Sample AI Instructions', 'mxchat');
4258 echo '</h3>';
4259 echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
4260 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">';
4261 echo '<line x1="18" y1="6" x2="6" y2="18"/>';
4262 echo '<line x1="6" y1="6" x2="18" y2="18"/>';
4263 echo '</svg>';
4264 echo '</button>';
4265 echo '</div>';
4266 echo '<div class="mxchat-instructions-modal-body">';
4267 echo '<div class="mxchat-instructions-content">';
4268 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:
4269
4270 # Response Style - CRITICALLY IMPORTANT
4271 - MAXIMUM LENGTH: 1-3 short sentences per response
4272 - Ultra-concise: Get straight to the answer with no filler
4273 - No introductions like "Sure!" or "I\'d be happy to help"
4274 - No phrases like "based on my knowledge" or "according to information"
4275 - No explanatory text before giving the answer
4276 - No summaries or repetition
4277 - Hyperlink all URLs
4278 - Respond in user\'s language
4279 - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
4280
4281 # Knowledge Base Requirements - PREVENT HALLUCINATIONS
4282 - ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
4283 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
4284 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
4285 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
4286 - Better to admit insufficient information than provide inaccurate answers');
4287 echo '</div>';
4288 echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
4289 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">';
4290 echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
4291 echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
4292 echo '</svg>';
4293 echo esc_html__('Copy Instructions', 'mxchat');
4294 echo '</button>';
4295 echo '</div>';
4296 echo '<div class="mxchat-instructions-modal-footer">';
4297 echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
4298 echo '</div>';
4299 echo '</div>';
4300 echo '</div>';
4301 }
4302
4303
4304 public function mxchat_model_callback() {
4305 // Define available models grouped by provider
4306 $models = array(
4307 esc_html__('Google Gemini Models', 'mxchat') => array(
4308 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
4309 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
4310 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
4311 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
4312 ),
4313 esc_html__('X.AI Models', 'mxchat') => array(
4314 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
4315 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
4316 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
4317 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
4318 'grok-2' => esc_html__('Grok 2', 'mxchat')
4319 ),
4320 esc_html__('DeepSeek Models', 'mxchat') => array(
4321 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4322 ),
4323 esc_html__('Claude Models', 'mxchat') => array(
4324 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
4325 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
4326 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
4327 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Intelligent)', 'mxchat'),
4328 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
4329 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4330 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4331 ),
4332 esc_html__('OpenAI Models', 'mxchat') => array(
4333 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4334 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4335 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4336 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4337 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4338 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4339 )
4340 );
4341
4342 // Retrieve the currently selected model from saved options
4343 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4344
4345 // Begin the select dropdown
4346 echo '<select id="model" name="model">';
4347
4348 // Iterate over groups of models
4349 foreach ($models as $group_label => $group_models) {
4350 echo '<optgroup label="' . esc_attr($group_label) . '">';
4351
4352 foreach ($group_models as $model_value => $model_label) {
4353 // All models enabled - no disabled attribute or Pro Only label
4354 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4355 }
4356
4357 echo '</optgroup>';
4358 }
4359
4360 // Close the select dropdown
4361 echo '</select>';
4362
4363 // Updated description to remove mention of Pro-only models
4364 echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
4365 }
4366
4367 public function enable_streaming_toggle_callback() {
4368 // Get value from options array, default to 'on'
4369 $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
4370 $checked = ($enabled === 'on') ? 'checked' : '';
4371
4372 echo '<label class="toggle-switch">';
4373 echo sprintf(
4374 '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
4375 esc_attr($checked)
4376 );
4377 echo '<span class="slider"></span>'; // Remove 'round' class to match working toggle
4378 echo '</label>';
4379 echo '<p class="description">' .
4380 esc_html__('Enable real-time streaming responses for supported models (OpenAI and Claude). When disabled, responses will load all at once.', 'mxchat') .
4381 '</p>';
4382 }
4383 // Callback function for embedding model selection
4384 public function embedding_model_callback() {
4385 $models = array(
4386 esc_html__('OpenAI Embeddings', 'mxchat') => array(
4387 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
4388 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
4389 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
4390 ),
4391 esc_html__('Voyage AI Embeddings', 'mxchat') => array(
4392 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
4393 ),
4394 esc_html__('Google Gemini Embeddings', 'mxchat') => array(
4395 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
4396 )
4397 );
4398 $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
4399 echo '<select id="embedding_model" name="embedding_model">';
4400 foreach ($models as $group_label => $group_models) {
4401 echo '<optgroup label="' . esc_attr($group_label) . '">';
4402 foreach ($group_models as $model_value => $model_label) {
4403 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4404 }
4405 echo '</optgroup>';
4406 }
4407 echo '</select>';
4408 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>';
4409 }
4410
4411
4412 public function mxchat_top_bar_title_callback() {
4413 // Retrieve the current value of the top bar title from saved options
4414 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4415
4416 // Render the input field
4417 echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
4418
4419 // Add a description
4420 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4421 }
4422 public function mxchat_ai_agent_text_callback() {
4423 // Retrieve the current value of the AI agent text from saved options
4424 $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
4425 // Render the input field
4426 echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
4427 // Add a description
4428 echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
4429 }
4430 public function enable_email_block_callback() {
4431 // Load full plugin options array
4432 $all_options = get_option('mxchat_options', []);
4433
4434 // Get the value, default to 'off'
4435 $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
4436
4437 // Check if it's 'on'
4438 $checked = ($enable_email_block === 'on') ? 'checked' : '';
4439
4440 echo '<label class="toggle-switch">';
4441 echo sprintf(
4442 '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
4443 esc_attr($checked)
4444 );
4445 echo '<span class="slider"></span>';
4446 echo '</label>';
4447 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>';
4448 }
4449
4450
4451 public function email_blocker_header_content_callback() {
4452 // Load the entire 'mxchat_options' array
4453 $all_options = get_option('mxchat_options', []);
4454
4455 // Retrieve the saved content or default to empty
4456 $content = isset($all_options['email_blocker_header_content'])
4457 ? $all_options['email_blocker_header_content']
4458 : '';
4459
4460 // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4461 echo '<textarea
4462 id="email_blocker_header_content"
4463 name="email_blocker_header_content"
4464 rows="5"
4465 cols="70"
4466 data-setting="email_blocker_header_content"
4467 >' . esc_textarea($content) . '</textarea>';
4468
4469 echo '<p class="description">';
4470 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');
4471 echo '</p>';
4472 }
4473
4474 public function email_blocker_button_text_callback() {
4475 // Load the entire 'mxchat_options' array
4476 $all_options = get_option('mxchat_options', []);
4477
4478 // Retrieve the saved button text or default to empty
4479 $button_text = isset($all_options['email_blocker_button_text'])
4480 ? $all_options['email_blocker_button_text']
4481 : '';
4482
4483 // Use esc_attr to safely render the existing text
4484 echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
4485
4486 echo '<p class="description">';
4487 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4488 echo '</p>';
4489 }
4490
4491
4492
4493 public function mxchat_intro_message_callback() {
4494 // Load the entire 'mxchat_options' array
4495 $all_options = get_option('mxchat_options', []);
4496 // Retrieve the saved intro message or use the default
4497 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4498 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4499 // Output the textarea with the saved value without escaping HTML
4500 ?>
4501 <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4502 <p class="description">
4503 <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4504 </p>
4505 <?php
4506 }
4507
4508 public function mxchat_input_copy_callback() {
4509 // Load the entire 'mxchat_options' array
4510 $all_options = get_option('mxchat_options', []);
4511
4512 // Retrieve the saved input copy or use the default value
4513 $default_copy = __('How can I assist?', 'mxchat');
4514 $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
4515
4516 // Output the input field with the saved value
4517 printf(
4518 '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
4519 esc_attr($input_copy),
4520 esc_attr__('How can I assist?', 'mxchat')
4521 );
4522
4523 // Output the description
4524 echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
4525 }
4526
4527
4528
4529 public function mxchat_user_message_font_color_callback() {
4530 $disabled = $this->is_activated ? '' : 'disabled';
4531 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4532
4533 echo '<div class="' . esc_attr($class) . '">';
4534 echo sprintf(
4535 '<input type="text"
4536 id="user_message_font_color"
4537 name="user_message_font_color"
4538 value="%s"
4539 class="my-color-field"
4540 data-default-color="#ffffff"
4541 %s />',
4542 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
4543 esc_attr($disabled)
4544 );
4545
4546 if (!$this->is_activated) {
4547 echo '<div class="pro-feature-overlay">';
4548 echo '<a href="https://mxchat.ai/" target="_blank">';
4549 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4550 echo '</a>';
4551 echo '</div>';
4552 }
4553 echo '</div>';
4554 }
4555
4556 public function mxchat_bot_message_bg_color_callback() {
4557 $disabled = $this->is_activated ? '' : 'disabled';
4558 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4559
4560 echo '<div class="' . esc_attr($class) . '">';
4561 echo sprintf(
4562 '<input type="text"
4563 id="bot_message_bg_color"
4564 name="bot_message_bg_color"
4565 value="%s"
4566 class="my-color-field"
4567 data-default-color="#e1e1e1"
4568 %s />',
4569 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
4570 esc_attr($disabled)
4571 );
4572
4573 if (!$this->is_activated) {
4574 echo '<div class="pro-feature-overlay">';
4575 echo '<a href="https://mxchat.ai/" target="_blank">';
4576 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4577 echo '</a>';
4578 echo '</div>';
4579 }
4580 echo '</div>';
4581 }
4582
4583 public function mxchat_bot_message_font_color_callback() {
4584 $disabled = $this->is_activated ? '' : 'disabled';
4585 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4586
4587 echo '<div class="' . esc_attr($class) . '">';
4588 echo sprintf(
4589 '<input type="text"
4590 id="bot_message_font_color"
4591 name="bot_message_font_color"
4592 value="%s"
4593 class="my-color-field"
4594 data-default-color="#333333"
4595 %s />',
4596 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
4597 esc_attr($disabled)
4598 );
4599
4600 if (!$this->is_activated) {
4601 echo '<div class="pro-feature-overlay">';
4602 echo '<a href="https://mxchat.ai/" target="_blank">';
4603 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4604 echo '</a>';
4605 echo '</div>';
4606 }
4607 echo '</div>';
4608 }
4609
4610 public function mxchat_live_agent_message_bg_color_callback() {
4611 $disabled = $this->is_activated ? '' : 'disabled';
4612 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4613
4614 echo '<div class="' . esc_attr($class) . '">';
4615 echo sprintf(
4616 '<input type="text"
4617 id="live_agent_message_bg_color"
4618 name="live_agent_message_bg_color"
4619 value="%s"
4620 class="my-color-field"
4621 data-default-color="#ffffff"
4622 %s />',
4623 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
4624 esc_attr($disabled)
4625 );
4626
4627 if (!$this->is_activated) {
4628 echo '<div class="pro-feature-overlay">';
4629 echo '<a href="https://mxchat.ai/" target="_blank">';
4630 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4631 echo '</a>';
4632 echo '</div>';
4633 }
4634 echo '</div>';
4635 }
4636
4637 public function mxchat_live_agent_message_font_color_callback() {
4638 $disabled = $this->is_activated ? '' : 'disabled';
4639 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4640
4641 echo '<div class="' . esc_attr($class) . '">';
4642 echo sprintf(
4643 '<input type="text"
4644 id="live_agent_message_font_color"
4645 name="live_agent_message_font_color"
4646 value="%s"
4647 class="my-color-field"
4648 data-default-color="#333333"
4649 %s />',
4650 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
4651 esc_attr($disabled)
4652 );
4653
4654 if (!$this->is_activated) {
4655 echo '<div class="pro-feature-overlay">';
4656 echo '<a href="https://mxchat.ai/" target="_blank">';
4657 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4658 echo '</a>';
4659 echo '</div>';
4660 }
4661 echo '</div>';
4662 }
4663
4664 public function mxchat_mode_indicator_bg_color_callback() {
4665 $disabled = $this->is_activated ? '' : 'disabled';
4666 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4667
4668 echo '<div class="' . esc_attr($class) . '">';
4669 echo sprintf(
4670 '<input type="text"
4671 id="mode_indicator_bg_color"
4672 name="mode_indicator_bg_color"
4673 value="%s"
4674 class="my-color-field"
4675 data-default-color="#767676"
4676 %s />',
4677 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
4678 esc_attr($disabled)
4679 );
4680
4681 if (!$this->is_activated) {
4682 echo '<div class="pro-feature-overlay">';
4683 echo '<a href="https://mxchat.ai/" target="_blank">';
4684 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4685 echo '</a>';
4686 echo '</div>';
4687 }
4688 echo '</div>';
4689 }
4690
4691 public function mxchat_mode_indicator_font_color_callback() {
4692 $disabled = $this->is_activated ? '' : 'disabled';
4693 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4694
4695 echo '<div class="' . esc_attr($class) . '">';
4696 echo sprintf(
4697 '<input type="text"
4698 id="mode_indicator_font_color"
4699 name="mode_indicator_font_color"
4700 value="%s"
4701 class="my-color-field"
4702 data-default-color="#ffffff"
4703 %s />',
4704 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
4705 esc_attr($disabled)
4706 );
4707
4708 if (!$this->is_activated) {
4709 echo '<div class="pro-feature-overlay">';
4710 echo '<a href="https://mxchat.ai/" target="_blank">';
4711 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4712 echo '</a>';
4713 echo '</div>';
4714 }
4715 echo '</div>';
4716 }
4717
4718 public function mxchat_toolbar_icon_color_callback() {
4719 $disabled = $this->is_activated ? '' : 'disabled';
4720 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4721
4722 echo '<div class="' . esc_attr($class) . '">';
4723 echo sprintf(
4724 '<input type="text"
4725 id="toolbar_icon_color"
4726 name="toolbar_icon_color"
4727 value="%s"
4728 class="my-color-field"
4729 data-default-color="#212121"
4730 %s />',
4731 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
4732 esc_attr($disabled)
4733 );
4734
4735 if (!$this->is_activated) {
4736 echo '<div class="pro-feature-overlay">';
4737 echo '<a href="https://mxchat.ai/" target="_blank">';
4738 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4739 echo '</a>';
4740 echo '</div>';
4741 }
4742 echo '</div>';
4743 }
4744
4745 public function mxchat_top_bar_bg_color_callback() {
4746 $disabled = $this->is_activated ? '' : 'disabled';
4747 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4748
4749 echo '<div class="' . esc_attr($class) . '">';
4750 echo sprintf(
4751 '<input type="text"
4752 id="top_bar_bg_color"
4753 name="top_bar_bg_color"
4754 value="%s"
4755 class="my-color-field"
4756 data-default-color="#00b294"
4757 %s />',
4758 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
4759 esc_attr($disabled)
4760 );
4761
4762 if (!$this->is_activated) {
4763 echo '<div class="pro-feature-overlay">';
4764 echo '<a href="https://mxchat.ai/" target="_blank">';
4765 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4766 echo '</a>';
4767 echo '</div>';
4768 }
4769 echo '</div>';
4770 }
4771
4772 public function mxchat_send_button_font_color_callback() {
4773 $disabled = $this->is_activated ? '' : 'disabled';
4774 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4775
4776 echo '<div class="' . esc_attr($class) . '">';
4777 echo sprintf(
4778 '<input type="text"
4779 id="send_button_font_color"
4780 name="send_button_font_color"
4781 value="%s"
4782 class="my-color-field"
4783 data-default-color="#ffffff"
4784 %s />',
4785 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
4786 esc_attr($disabled)
4787 );
4788
4789 if (!$this->is_activated) {
4790 echo '<div class="pro-feature-overlay">';
4791 echo '<a href="https://mxchat.ai/" target="_blank">';
4792 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4793 echo '</a>';
4794 echo '</div>';
4795 }
4796 echo '</div>';
4797 }
4798
4799 public function mxchat_chatbot_background_color_callback() {
4800 $disabled = $this->is_activated ? '' : 'disabled';
4801 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4802
4803 echo '<div class="' . esc_attr($class) . '">';
4804 echo sprintf(
4805 '<input type="text"
4806 id="chatbot_background_color"
4807 name="chatbot_background_color"
4808 value="%s"
4809 class="my-color-field"
4810 data-default-color="#000000"
4811 %s />',
4812 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
4813 esc_attr($disabled)
4814 );
4815
4816 if (!$this->is_activated) {
4817 echo '<div class="pro-feature-overlay">';
4818 echo '<a href="https://mxchat.ai/" target="_blank">';
4819 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4820 echo '</a>';
4821 echo '</div>';
4822 }
4823 echo '</div>';
4824 }
4825
4826 public function mxchat_icon_color_callback() {
4827 $disabled = $this->is_activated ? '' : 'disabled';
4828 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4829
4830 echo '<div class="' . esc_attr($class) . '">';
4831 echo sprintf(
4832 '<input type="text"
4833 id="icon_color"
4834 name="icon_color"
4835 value="%s"
4836 class="my-color-field"
4837 data-default-color="#ffffff"
4838 %s />',
4839 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
4840 esc_attr($disabled)
4841 );
4842
4843 if (!$this->is_activated) {
4844 echo '<div class="pro-feature-overlay">';
4845 echo '<a href="https://mxchat.ai/" target="_blank">';
4846 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4847 echo '</a>';
4848 echo '</div>';
4849 }
4850 echo '</div>';
4851 }
4852
4853 public function mxchat_custom_icon_callback() {
4854 $disabled = $this->is_activated ? '' : 'disabled';
4855 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4856 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
4857
4858 echo '<div class="' . esc_attr($class) . '">';
4859 echo sprintf(
4860 '<input type="url"
4861 id="custom_icon"
4862 name="custom_icon"
4863 value="%s"
4864 placeholder="%s"
4865 class="regular-text"
4866 %s />',
4867 $custom_icon_url,
4868 esc_attr__('Enter PNG URL', 'mxchat'),
4869 esc_attr($disabled)
4870 );
4871
4872 // Preview container for the icon
4873 if (!empty($custom_icon_url)) {
4874 echo '<div class="icon-preview" style="margin-top: 10px;">';
4875 echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4876 echo '</div>';
4877 }
4878
4879 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4880
4881 if (!$this->is_activated) {
4882 echo '<div class="pro-feature-overlay">';
4883 echo '<a href="https://mxchat.ai/" target="_blank">';
4884 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4885 echo '</a>';
4886 echo '</div>';
4887 }
4888 echo '</div>';
4889 }
4890
4891 public function mxchat_title_icon_callback() {
4892 $disabled = $this->is_activated ? '' : 'disabled';
4893 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4894 // Fixed the variable reference - it was using custom_icon instead of title_icon
4895 $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
4896
4897 echo '<div class="' . esc_attr($class) . '">';
4898 echo sprintf(
4899 '<input type="url"
4900 id="title_icon"
4901 name="title_icon"
4902 value="%s"
4903 placeholder="%s"
4904 class="regular-text"
4905 %s />',
4906 $title_icon_url,
4907 esc_attr__('Enter PNG URL', 'mxchat'),
4908 esc_attr($disabled)
4909 );
4910
4911 // Preview container for the icon
4912 if (!empty($title_icon_url)) {
4913 echo '<div class="icon-preview" style="margin-top: 10px;">';
4914 echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4915 echo '</div>';
4916 }
4917
4918 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4919
4920 if (!$this->is_activated) {
4921 echo '<div class="pro-feature-overlay">';
4922 echo '<a href="https://mxchat.ai/" target="_blank">';
4923 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4924 echo '</a>';
4925 echo '</div>';
4926 }
4927 echo '</div>';
4928 }
4929
4930 public function mxchat_chat_input_font_color_callback() {
4931 $disabled = $this->is_activated ? '' : 'disabled';
4932 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4933
4934 echo '<div class="' . esc_attr($class) . '">';
4935 echo sprintf(
4936 '<input type="text"
4937 id="chat_input_font_color"
4938 name="chat_input_font_color"
4939 value="%s"
4940 class="my-color-field"
4941 data-default-color="#555555"
4942 %s />',
4943 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
4944 esc_attr($disabled)
4945 );
4946
4947 if (!$this->is_activated) {
4948 echo '<div class="pro-feature-overlay">';
4949 echo '<a href="https://mxchat.ai/" target="_blank">';
4950 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4951 echo '</a>';
4952 echo '</div>';
4953 }
4954 echo '</div>';
4955 }
4956
4957 public function mxchat_append_to_body_callback() {
4958 // Get value from options array, default to 'off'
4959 $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
4960 $checked = ($append_to_body === 'on') ? 'checked' : '';
4961
4962 echo '<label class="toggle-switch">';
4963 echo sprintf(
4964 '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
4965 esc_attr($checked)
4966 );
4967 echo '<span class="slider"></span>';
4968 echo '</label>';
4969 echo '<p class="description">' .
4970 esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
4971 '</p>';
4972
4973 }
4974
4975
4976
4977 public function mxchat_privacy_toggle_callback() {
4978 // Load from mxchat_options array
4979 $options = get_option('mxchat_options', []);
4980
4981 // Get privacy toggle value with fallback
4982 $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
4983 $checked = ($privacy_toggle === 'on') ? 'checked' : '';
4984
4985 // Get privacy text with fallback
4986 $privacy_text = isset($options['privacy_text'])
4987 ? $options['privacy_text']
4988 : __('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat');
4989
4990 // Output the toggle switch
4991 echo '<label class="toggle-switch">';
4992 echo sprintf(
4993 '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
4994 esc_attr($checked)
4995 );
4996 echo '<span class="slider"></span>';
4997 echo '</label>';
4998 echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
4999
5000 // Output the custom text input field
5001 echo sprintf(
5002 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
5003 esc_textarea($privacy_text)
5004 );
5005 echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
5006 }
5007
5008
5009 public function mxchat_complianz_toggle_callback() {
5010 // Load from mxchat_options array
5011 $options = get_option('mxchat_options', []);
5012
5013 // Get complianz toggle value with fallback
5014 $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
5015 $checked = ($complianz_toggle === 'on') ? 'checked' : '';
5016
5017 // Output the toggle switch
5018 echo '<label class="toggle-switch">';
5019 echo sprintf(
5020 '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
5021 esc_attr($checked)
5022 );
5023 echo '<span class="slider"></span>';
5024 echo '</label>';
5025
5026 echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
5027 }
5028
5029 public function mxchat_link_target_toggle_callback() {
5030 // Load from mxchat_options array
5031 $options = get_option('mxchat_options', []);
5032
5033 // Get link target toggle value with fallback
5034 $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
5035 $checked = ($link_target_toggle === 'on') ? 'checked' : '';
5036
5037 // Output the toggle switch
5038 echo '<label class="toggle-switch">';
5039 echo sprintf(
5040 '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
5041 esc_attr($checked)
5042 );
5043 echo '<span class="slider"></span>';
5044 echo '</label>';
5045 echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
5046 }
5047
5048 public function mxchat_chat_persistence_toggle_callback() {
5049 // Load from mxchat_options array
5050 $options = get_option('mxchat_options', []);
5051
5052 // Get chat persistence toggle value with fallback
5053 $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
5054 $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
5055
5056 // Output the toggle switch
5057 echo '<label class="toggle-switch">';
5058 echo sprintf(
5059 '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
5060 esc_attr($checked)
5061 );
5062 echo '<span class="slider"></span>';
5063 echo '</label>';
5064
5065 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>';
5066 }
5067
5068 public function mxchat_popular_question_1_callback() {
5069 // Load the full plugin options array
5070 $all_options = get_option('mxchat_options', []);
5071
5072 // Retrieve the specific option for popular_question_1
5073 $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
5074
5075 // Render the input field
5076 printf(
5077 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
5078 esc_attr($popular_question_1),
5079 esc_attr__('Enter Quick Question 1', 'mxchat')
5080 );
5081
5082 // Add a description for the field
5083 echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
5084 }
5085
5086
5087 public function mxchat_popular_question_2_callback() {
5088 // Load the full plugin options array
5089 $all_options = get_option('mxchat_options', []);
5090
5091 // Retrieve the specific option for popular_question_2
5092 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
5093
5094 // Render the input field
5095 printf(
5096 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
5097 esc_attr($popular_question_2),
5098 esc_attr__('Enter Quick Question 2', 'mxchat')
5099 );
5100
5101 // Add a description for the field
5102 echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
5103 }
5104
5105
5106 public function mxchat_popular_question_3_callback() {
5107 // Load the full plugin options array
5108 $all_options = get_option('mxchat_options', []);
5109
5110 // Retrieve the specific option for popular_question_3
5111 $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
5112
5113 // Render the input field
5114 printf(
5115 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
5116 esc_attr($popular_question_3),
5117 esc_attr(__('Enter Quick Question 3', 'mxchat'))
5118 );
5119
5120 // Add a description for the field
5121 echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
5122 }
5123
5124 public function mxchat_additional_popular_questions_callback() {
5125 $options = get_option('mxchat_options', []);
5126 $additional_questions = isset($options['additional_popular_questions'])
5127 ? $options['additional_popular_questions']
5128 : get_option('additional_popular_questions', array());
5129
5130 echo '<div id="mxchat-additional-questions-container">';
5131 if (!empty($additional_questions)) {
5132 foreach ($additional_questions as $index => $question) {
5133 printf(
5134 '<div class="mxchat-question-row">
5135 <input type="text" name="additional_popular_questions[]"
5136 value="%s"
5137 placeholder="%s"
5138 class="regular-text mxchat-question-input"
5139 data-question-index="%d" />
5140 <button type="button" class="button mxchat-remove-question"
5141 aria-label="%s">%s</button>
5142 </div>',
5143 esc_attr($question),
5144 esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
5145 $index,
5146 esc_attr(__('Remove question', 'mxchat')),
5147 esc_html__('Remove', 'mxchat')
5148 );
5149 }
5150 } else {
5151 printf(
5152 '<div class="mxchat-question-row">
5153 <input type="text" name="additional_popular_questions[]"
5154 value=""
5155 placeholder="%s"
5156 class="regular-text mxchat-question-input"
5157 data-question-index="0" />
5158 <button type="button" class="button mxchat-remove-question"
5159 aria-label="%s">%s</button>
5160 </div>',
5161 esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
5162 esc_attr(__('Remove question', 'mxchat')),
5163 esc_html__('Remove', 'mxchat')
5164 );
5165 }
5166 echo '</div>';
5167 printf(
5168 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5169 esc_attr(__('Add question', 'mxchat')),
5170 esc_html__('Add Question', 'mxchat')
5171 );
5172 echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5173 echo '</div>';
5174 }
5175
5176 public function mxchat_brave_api_key_callback() {
5177 $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
5178
5179 echo '<div class="api-key-wrapper">';
5180 echo sprintf(
5181 '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
5182 $brave_api_key
5183 );
5184 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5185 echo '</div>';
5186 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5187 }
5188
5189 public function mxchat_brave_image_count_callback() {
5190 $brave_image_count = isset($this->options['brave_image_count'])
5191 ? intval($this->options['brave_image_count'])
5192 : 4;
5193
5194 echo sprintf(
5195 '<input type="number" id="brave_image_count" name="brave_image_count"
5196 value="%d" min="1" max="6" class="small-text" />',
5197 $brave_image_count
5198 );
5199 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
5200 }
5201
5202 public function mxchat_brave_safe_search_callback() {
5203 $brave_safe_search = isset($this->options['brave_safe_search'])
5204 ? esc_attr($this->options['brave_safe_search'])
5205 : 'strict';
5206
5207 echo '<select id="brave_safe_search" name="brave_safe_search">';
5208 echo sprintf(
5209 '<option value="strict" %s>%s</option>',
5210 selected($brave_safe_search, 'strict', false),
5211 __('Strict', 'mxchat')
5212 );
5213 echo sprintf(
5214 '<option value="off" %s>%s</option>',
5215 selected($brave_safe_search, 'off', false),
5216 __('Off', 'mxchat')
5217 );
5218 echo '</select>';
5219 echo '<p class="description">' .
5220 esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
5221 '</p>';
5222 }
5223
5224 public function mxchat_brave_news_count_callback() {
5225 $brave_news_count = isset($this->options['brave_news_count'])
5226 ? intval($this->options['brave_news_count'])
5227 : 3;
5228
5229 echo sprintf(
5230 '<input type="number" id="brave_news_count" name="brave_news_count"
5231 value="%d" min="1" max="10" class="small-text" />',
5232 $brave_news_count
5233 );
5234 echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
5235 }
5236
5237 public function mxchat_brave_country_callback() {
5238 $brave_country = isset($this->options['brave_country'])
5239 ? esc_attr($this->options['brave_country'])
5240 : 'us';
5241
5242 echo sprintf(
5243 '<input type="text" id="brave_country" name="brave_country"
5244 value="%s" maxlength="2" class="small-text" />',
5245 $brave_country
5246 );
5247 echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
5248 }
5249
5250 public function mxchat_brave_language_callback() {
5251 $brave_language = isset($this->options['brave_language'])
5252 ? esc_attr($this->options['brave_language'])
5253 : 'en';
5254
5255 echo sprintf(
5256 '<input type="text" id="brave_language" name="brave_language"
5257 value="%s" maxlength="2" class="small-text" />',
5258 $brave_language
5259 );
5260 echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
5261 }
5262
5263
5264
5265
5266
5267 // Section Callback
5268 public function mxchat_pdf_intent_section_callback() {
5269 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5270 }
5271
5272 public function mxchat_chat_toolbar_toggle_callback() {
5273 // Get chat toolbar toggle value with fallback
5274 $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5275 $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5276
5277 // Output the toggle switch
5278 echo '<label class="toggle-switch">';
5279 echo sprintf(
5280 '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
5281 esc_attr($checked)
5282 );
5283 echo '<span class="slider"></span>';
5284 echo '</label>';
5285
5286 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>';
5287 }
5288 /**
5289 * Callback for PDF upload button toggle setting
5290 */
5291 public function mxchat_show_pdf_upload_button_callback() {
5292 // Get toggle value with fallback
5293 $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
5294 $checked = ($show_pdf_button === 'on') ? 'checked' : '';
5295
5296 // Output the toggle switch
5297 echo '<label class="toggle-switch">';
5298 echo sprintf(
5299 '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
5300 esc_attr($checked)
5301 );
5302 echo '<span class="slider"></span>';
5303 echo '</label>';
5304
5305 echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5306 }
5307 /**
5308 * Callback for Word upload button toggle setting
5309 */
5310 public function mxchat_show_word_upload_button_callback() {
5311 // Get toggle value with fallback
5312 $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
5313 $checked = ($show_word_button === 'on') ? 'checked' : '';
5314
5315 // Output the toggle switch
5316 echo '<label class="toggle-switch">';
5317 echo sprintf(
5318 '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
5319 esc_attr($checked)
5320 );
5321 echo '<span class="slider"></span>';
5322 echo '</label>';
5323
5324 echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5325 }
5326
5327 public function mxchat_pdf_intent_trigger_text_callback() {
5328 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5329
5330 echo sprintf(
5331 '<textarea id="pdf_intent_trigger_text"
5332 name="pdf_intent_trigger_text"
5333 rows="3"
5334 cols="50"
5335 placeholder="%s">%s</textarea>',
5336 esc_attr__('Enter trigger text', 'mxchat'),
5337 isset($this->options['pdf_intent_trigger_text'])
5338 ? esc_textarea($this->options['pdf_intent_trigger_text'])
5339 : esc_textarea($default_text)
5340 );
5341 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5342 }
5343
5344 public function mxchat_pdf_intent_success_text_callback() {
5345 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5346
5347 echo sprintf(
5348 '<textarea id="pdf_intent_success_text"
5349 name="pdf_intent_success_text"
5350 rows="3"
5351 cols="50"
5352 placeholder="%s">%s</textarea>',
5353 esc_attr__('Enter success text', 'mxchat'),
5354 isset($this->options['pdf_intent_success_text'])
5355 ? esc_textarea($this->options['pdf_intent_success_text'])
5356 : esc_textarea($default_text)
5357 );
5358 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5359 }
5360
5361 public function mxchat_pdf_intent_error_text_callback() {
5362 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5363
5364 echo sprintf(
5365 '<textarea id="pdf_intent_error_text"
5366 name="pdf_intent_error_text"
5367 rows="3"
5368 cols="50"
5369 placeholder="%s">%s</textarea>',
5370 esc_attr__('Enter error text', 'mxchat'),
5371 isset($this->options['pdf_intent_error_text'])
5372 ? esc_textarea($this->options['pdf_intent_error_text'])
5373 : esc_textarea($default_text)
5374 );
5375 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5376 }
5377
5378 public function mxchat_pdf_max_pages_callback() {
5379 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5380
5381 echo sprintf(
5382 '<input type="range"
5383 id="pdf_max_pages"
5384 name="pdf_max_pages"
5385 min="1"
5386 max="69"
5387 value="%d"
5388 class="range-slider" />',
5389 esc_attr($max_pages)
5390 );
5391 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5392 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5393 }
5394
5395 public function mxchat_live_agent_status_callback() {
5396 // Always get fresh options instead of using cached $this->options
5397 $fresh_options = get_option('mxchat_options');
5398 $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
5399
5400 echo '<label class="toggle-switch">';
5401 echo sprintf(
5402 '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
5403 checked($status, 'on', false)
5404 );
5405 echo '<span class="slider"></span>';
5406 echo '</label>';
5407 echo '<label for="live_agent_status" class="mxchat-status-label">';
5408 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5409 echo '</label>';
5410 }
5411
5412 public function mxchat_live_agent_away_message_callback() {
5413 $message = isset($this->options['live_agent_away_message'])
5414 ? $this->options['live_agent_away_message']
5415 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5416
5417 printf(
5418 '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
5419 esc_textarea($message)
5420 );
5421 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5422 }
5423
5424 public function mxchat_live_agent_notification_message_callback() {
5425 $message = isset($this->options['live_agent_notification_message'])
5426 ? $this->options['live_agent_notification_message']
5427 : __('Live agent has been notified.', 'mxchat');
5428
5429 printf(
5430 '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
5431 esc_textarea($message)
5432 );
5433 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5434 }
5435
5436 public function mxchat_live_agent_webhook_url_callback() {
5437 $webhook_url = isset($this->options['live_agent_webhook_url'])
5438 ? esc_url($this->options['live_agent_webhook_url'])
5439 : esc_url(get_option('live_agent_webhook_url', ''));
5440
5441 printf(
5442 '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
5443 $webhook_url
5444 );
5445 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5446 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
5447 }
5448
5449 public function mxchat_live_agent_secret_key_callback() {
5450 printf(
5451 '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
5452 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
5453 );
5454 echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5455 echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5456 }
5457
5458 public function mxchat_live_agent_bot_token_callback() {
5459 printf(
5460 '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
5461 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
5462 );
5463 echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5464 echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5465 }
5466
5467 public function mxchat_live_agent_user_ids_callback() {
5468 $user_ids = isset($this->options['live_agent_user_ids'])
5469 ? esc_textarea($this->options['live_agent_user_ids'])
5470 : '';
5471
5472 printf(
5473 '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
5474 $user_ids
5475 );
5476 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>';
5477 }
5478
5479 public function mxchat_similarity_threshold_callback() {
5480 // Load from mxchat_options array
5481 $options = get_option('mxchat_options', []);
5482
5483 // Get value from options array with default of 80
5484 $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
5485
5486 echo '<div class="slider-container">';
5487 echo sprintf(
5488 '<input type="range"
5489 id="similarity_threshold"
5490 name="similarity_threshold"
5491 min="20"
5492 max="85"
5493 step="1"
5494 value="%s"
5495 class="range-slider" />',
5496 esc_attr($threshold)
5497 );
5498 echo sprintf(
5499 '<span id="threshold_value" class="range-value">%s</span>',
5500 esc_html($threshold)
5501 );
5502 echo '</div>';
5503 echo '<p class="description">';
5504 echo sprintf(
5505 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'),
5506 '<a href="' . admin_url('admin.php?page=mxchat-addons') . '">',
5507 '</a>'
5508 );
5509 echo '</p>';
5510 }
5511
5512 public function mxchat_enqueue_admin_assets() {
5513 // Get plugin version (define this in your main plugin file)
5514 $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '2.2.7';
5515
5516 // Use file modification time for development (remove in production)
5517 if (defined('WP_DEBUG') && WP_DEBUG) {
5518 $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
5519 }
5520
5521 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
5522 $plugin_url = plugin_dir_url(__FILE__) . '../';
5523
5524 // Only load on MxChat pages
5525 if (strpos($current_page, 'mxchat') === false) {
5526 return;
5527 }
5528
5529 // Always load these on all MxChat pages
5530 $this->enqueue_core_admin_assets($plugin_url, $version);
5531 $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
5532 $this->localize_admin_scripts($current_page);
5533 }
5534 private function enqueue_core_admin_assets($plugin_url, $version) {
5535 // Core admin styles
5536 wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
5537 wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
5538
5539 // Core admin scripts
5540 wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
5541 }
5542 private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
5543 switch ($current_page) {
5544 case 'mxchat-prompts':
5545 // Knowledge processing page assets
5546 wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
5547 wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
5548 // NEW: Add the knowledge processing script
5549 wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
5550 break;
5551
5552 case 'mxchat-transcripts':
5553 wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
5554 wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
5555 break;
5556
5557 case 'mxchat-actions':
5558 wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
5559 break;
5560
5561 case 'mxchat-activation':
5562 wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
5563 break;
5564
5565 }
5566 }
5567 private function localize_admin_scripts($current_page) {
5568 // Base localization data for main admin script
5569 $base_data = array(
5570 'ajax_url' => admin_url('admin-ajax.php'),
5571 'nonce' => wp_create_nonce('mxchat_status_nonce'),
5572 'admin_url' => admin_url(),
5573 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5574 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5575 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5576 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
5577 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
5578 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
5579 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
5580 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
5581 'is_activated' => $this->is_activated ? '1' : '0',
5582 'status_refresh_interval' => 5000,
5583 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5584 'ajaxurl' => admin_url('admin-ajax.php') // For jQuery fallback
5585 );
5586
5587 // Localize main admin script with base data
5588 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
5589
5590 // Page-specific localizations
5591 $this->localize_page_specific_scripts($current_page);
5592 }
5593 private function localize_page_specific_scripts($current_page) {
5594 switch ($current_page) {
5595 case 'mxchat-prompts':
5596 // Status updater localization
5597 wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
5598 'ajax_url' => admin_url('admin-ajax.php'),
5599 'nonce' => wp_create_nonce('mxchat_status_nonce')
5600 ));
5601
5602 // Content selector localization
5603 wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
5604 'ajaxurl' => admin_url('admin-ajax.php'),
5605 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
5606 'i18n' => array(
5607 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
5608 'selectAll' => __('Select All', 'mxchat'),
5609 'process' => __('Process Selected', 'mxchat'),
5610 'cancel' => __('Cancel', 'mxchat'),
5611 'noResults' => __('No content found.', 'mxchat')
5612 )
5613 ));
5614
5615 // NEW: Knowledge processing script localization using mxchatAdmin
5616 wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
5617 'ajax_url' => admin_url('admin-ajax.php'),
5618 'status_nonce' => wp_create_nonce('mxchat_status_nonce'), // Note: changed 'nonce' to 'status_nonce'
5619 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
5620 'admin_url' => admin_url(),
5621 'ajaxurl' => admin_url('admin-ajax.php'),
5622 'status_refresh_interval' => 2000
5623 ));
5624 break;
5625
5626 case 'mxchat-activation':
5627 wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
5628 'ajax_url' => admin_url('admin-ajax.php'),
5629 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
5630 ));
5631 break;
5632
5633 case 'mxchat-settings':
5634 default:
5635 // Color picker and settings localization
5636 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5637 'ajax_url' => admin_url('admin-ajax.php'),
5638 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5639 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5640 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5641 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5642 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5643 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5644 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5645 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5646 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5647 'icon_color' => $this->options['icon_color'] ?? '#fff',
5648 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5649 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5650 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5651 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5652 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5653 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5654 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5655 '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'),
5656 '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'),
5657 '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'),
5658 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5659 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5660 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5661 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5662 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5663 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5664 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5665 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
5666 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
5667 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5668 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5669 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5670 ));
5671 break;
5672 }
5673
5674 // Additional localization that was in the original code
5675 wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
5676 'ajax_url' => admin_url('admin-ajax.php'),
5677 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5678 ));
5679 }
5680
5681 public function mxchat_sanitize($input) {
5682 $new_input = array();
5683
5684 if (isset($input['api_key'])) {
5685 $new_input['api_key'] = sanitize_text_field($input['api_key']);
5686 }
5687
5688 if (isset($input['similarity_threshold'])) {
5689 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5690 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5691 }
5692
5693 if (isset($input['xai_api_key'])) {
5694 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
5695 }
5696
5697 if (isset($input['claude_api_key'])) {
5698 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5699 }
5700
5701 if (isset($input['enable_streaming_toggle'])) {
5702 $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
5703 } else {
5704 // If checkbox not checked, it won't be in $input, so set to 'off'
5705 $new_input['enable_streaming_toggle'] = 'off';
5706 }
5707
5708
5709 if (isset($input['deepseek_api_key'])) {
5710 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5711 }
5712
5713 if (isset($input['gemini_api_key'])) {
5714 $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
5715 }
5716
5717 if (isset($input['enable_woocommerce_integration'])) {
5718 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5719 }
5720
5721 if (isset($input['privacy_toggle'])) {
5722 $new_input['privacy_toggle'] = $input['privacy_toggle'];
5723 }
5724
5725 if (isset($input['complianz_toggle'])) {
5726 $new_input['complianz_toggle'] = $input['complianz_toggle'];
5727 }
5728
5729 // Handle custom privacy text input
5730 if (isset($input['privacy_text'])) {
5731 // Allow basic HTML for links
5732 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
5733 }
5734
5735 if (isset($input['system_prompt_instructions'])) {
5736 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
5737 }
5738
5739 if (isset($input['mxchat_pro_email'])) {
5740 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
5741 }
5742
5743 if (isset($input['mxchat_activation_key'])) {
5744 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
5745 }
5746
5747 if (isset($input['append_to_body'])) {
5748 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5749 }
5750
5751 if (isset($input['top_bar_title'])) {
5752 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5753 }
5754
5755 if (isset($input['ai_agent_text'])) {
5756 $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
5757 }
5758
5759 if (isset($input['enable_email_block'])) {
5760 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5761 }
5762
5763 if (isset($input['email_blocker_header_content'])) {
5764 // wp_kses_post() allows standard HTML tags permitted by WordPress
5765 $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
5766 }
5767 if (isset($input['email_blocker_button_text'])) {
5768 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5769 }
5770
5771 if (isset($input['intro_message'])) {
5772 $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5773 }
5774
5775 if (isset($input['input_copy'])) {
5776 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
5777 }
5778
5779 if (isset($input['rate_limit_message'])) {
5780 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5781 }
5782
5783 // Handle the new rate limits format
5784 if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
5785 $new_input['rate_limits'] = array();
5786 $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
5787 $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
5788
5789 foreach ($input['rate_limits'] as $role_id => $settings) {
5790 $new_input['rate_limits'][$role_id] = array();
5791
5792 // Sanitize limit
5793 if (isset($settings['limit'])) {
5794 $limit = sanitize_text_field($settings['limit']);
5795 if (in_array($limit, $allowed_limits, true)) {
5796 $new_input['rate_limits'][$role_id]['limit'] = $limit;
5797 } else {
5798 $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
5799 }
5800 }
5801
5802 // Sanitize timeframe
5803 if (isset($settings['timeframe'])) {
5804 $timeframe = sanitize_text_field($settings['timeframe']);
5805 if (in_array($timeframe, $allowed_timeframes, true)) {
5806 $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
5807 } else {
5808 $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
5809 }
5810 }
5811
5812 // Sanitize message
5813 if (isset($settings['message'])) {
5814 $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
5815 }
5816 }
5817 }
5818
5819 if (isset($input['pre_chat_message'])) {
5820 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5821 }
5822
5823 if (isset($input['voyage_api_key'])) {
5824 $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
5825 }
5826
5827 // Add to your sanitize function
5828 if (isset($input['embedding_model'])) {
5829 $allowed_models = array(
5830 'text-embedding-ada-002',
5831 'text-embedding-3-small',
5832 'text-embedding-3-large',
5833 'voyage-3-large',
5834 'gemini-embedding-exp-03-07'
5835 );
5836 if (in_array($input['embedding_model'], $allowed_models)) {
5837 $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5838 }
5839 }
5840
5841 if (isset($input['model'])) {
5842 $allowed_models = array(
5843 'gemini-2.0-flash',
5844 'gemini-2.0-flash-lite',
5845 'gemini-1.5-pro',
5846 'gemini-1.5-flash',
5847 'grok-3-beta',
5848 'grok-3-fast-beta',
5849 'grok-3-mini-beta',
5850 'grok-3-mini-fast-beta',
5851 'grok-2',
5852 'deepseek-chat',
5853 'claude-opus-4-20250514',
5854 'claude-sonnet-4-20250514',
5855 'claude-3-7-sonnet-20250219',
5856 'claude-3-5-sonnet-20241022',
5857 'claude-3-opus-20240229',
5858 'claude-3-sonnet-20240229',
5859 'claude-3-haiku-20240307',
5860 'gpt-4o',
5861 'gpt-4.1-2025-04-14',
5862 'gpt-4o-mini',
5863 'gpt-4-turbo',
5864 'gpt-4',
5865 'gpt-3.5-turbo',
5866 );
5867 if (in_array($input['model'], $allowed_models)) {
5868 $new_input['model'] = sanitize_text_field($input['model']);
5869 }
5870 }
5871
5872 if (isset($input['close_button_color'])) {
5873 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5874 }
5875
5876 if (isset($input['chatbot_bg_color'])) {
5877 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
5878 }
5879
5880 if (isset($input['woocommerce_consumer_key'])) {
5881 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
5882 }
5883
5884 if (isset($input['woocommerce_consumer_secret'])) {
5885 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
5886 }
5887
5888 if (isset($input['user_message_bg_color'])) {
5889 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
5890 }
5891
5892 if (isset($input['user_message_font_color'])) {
5893 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
5894 }
5895
5896 if (isset($input['bot_message_bg_color'])) {
5897 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
5898 }
5899
5900 if (isset($input['bot_message_font_color'])) {
5901 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
5902 }
5903
5904 if (isset($input['live_agent_message_bg_color'])) {
5905 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
5906 }
5907
5908 if (isset($input['live_agent_message_font_color'])) {
5909 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
5910 }
5911
5912 if (isset($input['mode_indicator_bg_color'])) {
5913 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
5914 }
5915
5916 if (isset($input['mode_indicator_font_color'])) {
5917 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
5918 }
5919
5920 if (isset($input['toolbar_icon_color'])) {
5921 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
5922 }
5923
5924 if (isset($input['top_bar_bg_color'])) {
5925 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5926 }
5927
5928 if (isset($input['send_button_font_color'])) {
5929 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5930 }
5931
5932 if (isset($input['chatbot_background_color'])) {
5933 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
5934 }
5935
5936 if (isset($input['icon_color'])) {
5937 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
5938 }
5939
5940 if (isset($input['custom_icon'])) {
5941 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
5942 }
5943
5944 if (isset($input['title_icon'])) {
5945 $new_input['title_icon'] = esc_url_raw($input['title_icon']);
5946 }
5947
5948 if (isset($input['chat_input_font_color'])) {
5949 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
5950 }
5951
5952 // Sanitize link_target_toggle
5953 if (isset($input['link_target_toggle'])) {
5954 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
5955 }
5956
5957 // Sanitize Loops API Key
5958 if (isset($input['loops_api_key'])) {
5959 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
5960 }
5961
5962 if (isset($input['chat_persistence_toggle'])) {
5963 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
5964 }
5965
5966 if (isset($input['popular_question_1'])) {
5967 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
5968 }
5969
5970 if (isset($input['popular_question_2'])) {
5971 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
5972 }
5973
5974 if (isset($input['popular_question_3'])) {
5975 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
5976 }
5977
5978 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
5979 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
5980 }
5981
5982 // Sanitize Loops Mailing List
5983 if (isset($input['loops_mailing_list'])) {
5984 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5985 }
5986
5987 // Sanitize Triggered Phrase Response
5988 if (isset($input['triggered_phrase_response'])) {
5989 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5990 }
5991
5992 if (isset($input['email_capture_response'])) {
5993 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
5994 }
5995
5996 // Sanitize Brave Search Settings
5997 if (isset($input['brave_api_key'])) {
5998 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
5999 }
6000
6001 if (isset($input['brave_image_count'])) {
6002 $image_count = intval($input['brave_image_count']);
6003 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
6004 }
6005
6006 if (isset($input['brave_safe_search'])) {
6007 $allowed = array('strict', 'off');
6008 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
6009 }
6010
6011 if (isset($input['brave_news_count'])) {
6012 $news_count = intval($input['brave_news_count']);
6013 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
6014 }
6015
6016 if (isset($input['brave_country'])) {
6017 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
6018 }
6019
6020 if (isset($input['brave_language'])) {
6021 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
6022 }
6023
6024 if (isset($input['chat_toolbar_toggle'])) {
6025 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
6026 }
6027
6028 // Sanitize PDF upload button toggle
6029 if (isset($input['show_pdf_upload_button'])) {
6030 $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
6031 } else {
6032 $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
6033 }
6034
6035 // Sanitize Word upload button toggle
6036 if (isset($input['show_word_upload_button'])) {
6037 $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
6038 } else {
6039 $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
6040 }
6041
6042 if (isset($input['pdf_intent_trigger_text'])) {
6043 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
6044 }
6045
6046 if (isset($input['pdf_intent_success_text'])) {
6047 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
6048 }
6049
6050 if (isset($input['pdf_intent_error_text'])) {
6051 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
6052 }
6053
6054 if (isset($input['pdf_max_pages'])) {
6055 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
6056 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
6057 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
6058 }
6059 }
6060
6061 if (isset($input['live_agent_webhook_url'])) {
6062 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
6063 }
6064 if (isset($input['live_agent_secret_key'])) {
6065 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
6066 }
6067
6068 // Live Agent Integration
6069 if (isset($input['live_agent_bot_token'])) {
6070 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
6071 }
6072
6073 if (isset($input['live_agent_user_ids'])) {
6074 $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
6075 }
6076
6077 if (isset($input['live_agent_status'])) {
6078 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
6079 }
6080 if (isset($input['live_agent_away_message'])) {
6081 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
6082 }
6083 if (isset($input['live_agent_notification_message'])) {
6084 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
6085 }
6086
6087 return $new_input;
6088 }
6089
6090
6091 // Method to append the chatbot to the body
6092 public function mxchat_append_chatbot_to_body() {
6093 $options = get_option('mxchat_options');
6094 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
6095 echo do_shortcode('[mxchat_chatbot floating="yes"]');
6096 }
6097 }
6098
6099
6100
6101
6102
6103 private function mxchat_fetch_loops_mailing_lists($api_key) {
6104 $url = 'https://app.loops.so/api/v1/lists';
6105 $response = wp_remote_get($url, array(
6106 'headers' => array(
6107 'Authorization' => 'Bearer ' . $api_key,
6108 'Content-Type' => 'application/json'
6109 )
6110 ));
6111
6112 if (is_wp_error($response)) {
6113 return array();
6114 }
6115
6116 $body = wp_remote_retrieve_body($response);
6117 $lists = json_decode($body, true);
6118
6119 return isset($lists) && is_array($lists) ? $lists : array();
6120 }
6121
6122 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
6123 if (empty($vec1) || empty($vec2)) {
6124 return 0.0;
6125 }
6126
6127 $dot_product = 0.0;
6128 $norm_a = 0.0;
6129 $norm_b = 0.0;
6130
6131 for ($i = 0; $i < count($vec1); $i++) {
6132 $dot_product += $vec1[$i] * $vec2[$i];
6133 $norm_a += pow($vec1[$i], 2);
6134 $norm_b += pow($vec2[$i], 2);
6135 }
6136
6137 if ($norm_a == 0.0 || $norm_b == 0.0) {
6138 return 0.0;
6139 } else {
6140 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
6141 }
6142 }
6143
6144 /**
6145 * Validates nonce and deletes all chat prompts
6146 */
6147 public function mxchat_handle_delete_all_prompts() {
6148 // Verify nonce
6149 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
6150 wp_die(__('Nonce verification failed.', 'mxchat'));
6151 }
6152 // Check permissions
6153 if (!current_user_can('manage_options')) {
6154 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
6155 }
6156 $success = true;
6157 $error_messages = array();
6158 // Check if Pinecone is enabled and configured
6159 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6160 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6161 if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
6162 //error_log('[MXCHAT-DELETE] Pinecone is enabled - deleting all from Pinecone');
6163 // Get Pinecone configuration
6164 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6165 $pinecone_manager = new MxChat_Pinecone_Manager();
6166 $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
6167 if (!$result['success']) {
6168 $success = false;
6169 $error_messages[] = $result['message'];
6170 }
6171 // Clear Pinecone vector cache
6172 delete_option('mxchat_pinecone_vector_ids_cache');
6173 // Clear processed content caches
6174 delete_option('mxchat_pinecone_processed_cache');
6175 delete_option('mxchat_processed_content_cache');
6176
6177 // CLEAR THE PINECONE RECORDS CACHE (NEW LINE)
6178 delete_transient('mxchat_pinecone_recent_1k_cache');
6179
6180 } else {
6181 //error_log('[MXCHAT-DELETE] Pinecone not enabled - deleting from WordPress database');
6182 // Delete from WordPress database
6183 global $wpdb;
6184 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6185 $result = $wpdb->query("DELETE FROM {$table_name}");
6186 if ($result === false) {
6187 $success = false;
6188 $error_messages[] = 'Failed to delete from WordPress database';
6189 }
6190 }
6191 // Clear relevant cache
6192 wp_cache_delete('all_prompts', 'mxchat_prompts');
6193 // Set appropriate admin notice
6194 if ($success) {
6195 set_transient('mxchat_admin_notice_success',
6196 __('All prompts deleted successfully.', 'mxchat'), 30);
6197 } else {
6198 set_transient('mxchat_admin_notice_error',
6199 __('Failed to delete all prompts: ', 'mxchat') . implode(', ', $error_messages), 30);
6200 }
6201 // Redirect back with a success message
6202 $redirect_url = add_query_arg(array(
6203 'page' => 'mxchat-prompts',
6204 'all_deleted' => $success ? 'true' : 'false'
6205 ), admin_url('admin.php'));
6206 wp_safe_redirect($redirect_url);
6207 exit;
6208 }
6209
6210 /**
6211 * Handles deletion prompt with nonce validation
6212 */
6213 public function mxchat_handle_delete_prompt() {
6214 // Sanitize and validate nonce
6215 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
6216 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
6217 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
6218 }
6219
6220 // Check permissions
6221 if (!current_user_can('manage_options')) {
6222 wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
6223 }
6224
6225 // Get ID and source parameters
6226 $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
6227 $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
6228
6229 if (empty($id)) {
6230 wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
6231 }
6232
6233 $success = false;
6234 $error_message = '';
6235
6236 // Check if Pinecone is enabled and determine source automatically if not specified
6237 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6238 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6239
6240 // If source is not specified, determine based on Pinecone configuration
6241 if (empty($source)) {
6242 $source = $use_pinecone ? 'pinecone' : 'wordpress';
6243 }
6244
6245 if ($source === 'pinecone' || $use_pinecone) {
6246 //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
6247
6248 // Handle Pinecone deletion
6249 if (empty($pinecone_options['mxchat_pinecone_host']) ||
6250 empty($pinecone_options['mxchat_pinecone_api_key'])) {
6251 wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
6252 }
6253
6254 // Delete from Pinecone using the vector ID directly
6255 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
6256 $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
6257 $id,
6258 $pinecone_options['mxchat_pinecone_api_key'],
6259 $pinecone_options['mxchat_pinecone_host']
6260 );
6261 if ($result['success']) {
6262 $success = true;
6263
6264 // Remove from vector cache
6265 $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
6266 $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
6267
6268 set_transient('mxchat_admin_notice_success',
6269 esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
6270 } else {
6271 $error_message = $result['message'];
6272 set_transient('mxchat_admin_notice_error',
6273 esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
6274 }
6275 } else {
6276 //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
6277
6278 // Handle WordPress database deletion
6279 global $wpdb;
6280 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6281
6282 // Clear cache and delete prompt
6283 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
6284
6285 $result = $wpdb->delete(
6286 $table_name,
6287 array('id' => intval($id)),
6288 array('%d')
6289 );
6290
6291 if ($result !== false) {
6292 $success = true;
6293 set_transient('mxchat_admin_notice_success',
6294 esc_html__('Entry deleted successfully.', 'mxchat'), 30);
6295 } else {
6296 set_transient('mxchat_admin_notice_error',
6297 esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
6298 }
6299 }
6300
6301 // Redirect back to the prompts page
6302 wp_safe_redirect(add_query_arg(
6303 array(
6304 'page' => 'mxchat-prompts',
6305 'deleted' => $success ? 'true' : 'false'
6306 ),
6307 admin_url('admin.php')
6308 ));
6309 exit;
6310 }
6311
6312 /**
6313 * Validates user permissions for editing chat settings
6314 */
6315 public function mxchat_handle_edit_intent() {
6316 // Security checks (nonce and permissions)
6317 if (!current_user_can('manage_options')) {
6318 wp_die(esc_html__('Unauthorized user', 'mxchat'));
6319 }
6320 check_admin_referer('mxchat_edit_intent');
6321
6322 // Get POST data
6323 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
6324 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
6325 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
6326 $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
6327 $similarity_threshold = min(95, max(70, $threshold_percentage)) / 100; // Convert to 0.70–0.95
6328
6329 // Validate inputs
6330 if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
6331 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
6332 return;
6333 }
6334
6335 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
6336 if (empty($phrases_array)) {
6337 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
6338 return;
6339 }
6340
6341 // Generate embeddings with improved error handling
6342 $vectors = [];
6343 $failed_phrases = [];
6344
6345 foreach ($phrases_array as $phrase) {
6346 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
6347 if (is_array($embedding_vector)) {
6348 $vectors[] = $embedding_vector;
6349 } else {
6350 $failed_phrases[] = $phrase;
6351 }
6352 }
6353
6354 if (!empty($failed_phrases)) {
6355 $this->handle_embedding_error(
6356 sprintf(
6357 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
6358 implode(', ', $failed_phrases)
6359 )
6360 );
6361 return;
6362 }
6363
6364 if (empty($vectors)) {
6365 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
6366 return;
6367 }
6368
6369 $combined_vector = $this->mxchat_average_vectors($vectors);
6370 $serialized_vector = maybe_serialize($combined_vector);
6371
6372 // Update the database
6373 global $wpdb;
6374 $table_name = $wpdb->prefix . 'mxchat_intents';
6375
6376 $result = $wpdb->update(
6377 $table_name,
6378 array(
6379 'intent_label' => $intent_label,
6380 'phrases' => implode(', ', $phrases_array),
6381 'embedding_vector' => $serialized_vector,
6382 'similarity_threshold' => $similarity_threshold
6383 ),
6384 array('id' => $intent_id),
6385 array('%s', '%s', '%s', '%f'), // Format: string, string, string, float
6386 array('%d') // Where format: integer
6387 );
6388
6389 if (false === $result) {
6390 $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
6391 return;
6392 }
6393
6394 // Set success message and redirect
6395 set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
6396
6397 $redirect_url = add_query_arg(
6398 array(
6399 'page' => 'mxchat-actions'
6400 ),
6401 admin_url('admin.php')
6402 );
6403 wp_safe_redirect($redirect_url);
6404 exit;
6405 }
6406 /**
6407 * Checks user permissions for managing options
6408 */
6409 public function mxchat_handle_add_intent() {
6410 if (!current_user_can('manage_options')) {
6411 wp_die(esc_html__('Unauthorized user', 'mxchat'));
6412 }
6413 check_admin_referer('mxchat_add_intent_nonce');
6414
6415 global $wpdb;
6416 $table_name = $wpdb->prefix . 'mxchat_intents';
6417
6418 // Sanitize and get form data
6419 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
6420 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
6421 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
6422
6423 // Get similarity threshold from form (convert percentage to decimal)
6424 $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
6425
6426 // Validate required fields
6427 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
6428 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
6429 return;
6430 }
6431
6432 // Validate callback function
6433 $available_callbacks = $this->mxchat_get_available_callbacks();
6434 if (!array_key_exists($callback_function, $available_callbacks)) {
6435 $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
6436 return;
6437 }
6438
6439 // Check Pro requirements
6440 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
6441 if ($is_pro_only && !$this->is_activated) {
6442 $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
6443 return;
6444 }
6445
6446 // Process phrases
6447 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
6448 if (empty($phrases_array)) {
6449 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
6450 return;
6451 }
6452
6453 // Generate embeddings with improved error handling
6454 $vectors = [];
6455 $failed_phrases = [];
6456 foreach ($phrases_array as $phrase) {
6457 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
6458 if (is_array($embedding_vector)) {
6459 $vectors[] = $embedding_vector;
6460 } else {
6461 $failed_phrases[] = $phrase;
6462 }
6463 }
6464
6465 // Check for embedding failures
6466 if (!empty($failed_phrases)) {
6467 $this->handle_embedding_error(
6468 sprintf(
6469 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
6470 implode(', ', $failed_phrases)
6471 )
6472 );
6473 return;
6474 }
6475
6476 if (empty($vectors)) {
6477 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
6478 return;
6479 }
6480
6481 // Create combined vector and insert into database
6482 $combined_vector = $this->mxchat_average_vectors($vectors);
6483 $serialized_vector = maybe_serialize($combined_vector);
6484
6485 $result = $wpdb->insert($table_name, [
6486 'intent_label' => $intent_label,
6487 'phrases' => implode(', ', $phrases_array),
6488 'embedding_vector' => $serialized_vector,
6489 'callback_function' => $callback_function,
6490 'similarity_threshold' => $similarity_threshold, // Now uses the actual form value
6491 ]);
6492
6493 if ($result === false) {
6494 $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
6495 return;
6496 }
6497
6498 // Set success message and redirect
6499 set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
6500 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
6501 exit;
6502 }
6503
6504 /**
6505 * Averages multiple vectors into a single vector
6506 */
6507 private function mxchat_average_vectors($vectors) {
6508 $vector_length = count($vectors[0]);
6509 $sum_vector = array_fill(0, $vector_length, 0);
6510
6511 foreach ($vectors as $vector) {
6512 for ($i = 0; $i < $vector_length; $i++) {
6513 $sum_vector[$i] += $vector[$i];
6514 }
6515 }
6516
6517 // Divide each component by the number of vectors to get the average
6518 $num_vectors = count($vectors);
6519 for ($i = 0; $i < $vector_length; $i++) {
6520 $sum_vector[$i] /= $num_vectors;
6521 }
6522
6523 return $sum_vector;
6524 }
6525
6526
6527
6528
6529 /**
6530 * Generates embeddings from input text for MXChat
6531 */
6532 public function mxchat_generate_embedding($text) {
6533 // Enable detailed logging for debugging
6534 //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
6535 //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
6536
6537 $options = get_option('mxchat_options');
6538 $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
6539 //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
6540
6541 // Determine provider and endpoint
6542 if (strpos($selected_model, 'voyage') === 0) {
6543 $api_key = $options['voyage_api_key'] ?? '';
6544 $endpoint = 'https://api.voyageai.com/v1/embeddings';
6545 $provider_name = 'Voyage AI';
6546 //error_log('[MXCHAT-EMBED] Using Voyage AI API');
6547 } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
6548 $api_key = $options['gemini_api_key'] ?? '';
6549 $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
6550 $provider_name = 'Google Gemini';
6551 //error_log('[MXCHAT-EMBED] Using Google Gemini API');
6552 } else {
6553 $api_key = $options['api_key'] ?? '';
6554 $endpoint = 'https://api.openai.com/v1/embeddings';
6555 $provider_name = 'OpenAI';
6556 //error_log('[MXCHAT-EMBED] Using OpenAI API');
6557 }
6558
6559 //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
6560
6561 if (empty($api_key)) {
6562 $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
6563 //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
6564 return $error_message;
6565 }
6566
6567 // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
6568 $estimated_tokens = ceil(str_word_count($text) / 0.75);
6569 //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
6570
6571 if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
6572 //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
6573 // Consider truncating text here
6574 }
6575
6576 // Prepare request body based on provider
6577 if (strpos($selected_model, 'gemini-embedding') === 0) {
6578 // Gemini API format
6579 $request_body = array(
6580 'model' => 'models/' . $selected_model,
6581 'content' => array(
6582 'parts' => array(
6583 array('text' => $text)
6584 )
6585 )
6586 );
6587
6588 // Set output dimensionality to 1536 for consistency with other models
6589 $request_body['outputDimensionality'] = 1536;
6590 } else {
6591 // OpenAI/Voyage API format
6592 $request_body = array(
6593 'model' => $selected_model,
6594 'input' => $text
6595 );
6596
6597 // Add output_dimension for voyage-3-large model
6598 if ($selected_model === 'voyage-3-large') {
6599 $request_body['output_dimension'] = 2048;
6600 }
6601 }
6602
6603 //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
6604
6605 // Prepare headers based on provider
6606 if (strpos($selected_model, 'gemini-embedding') === 0) {
6607 // Gemini uses API key as query parameter
6608 $endpoint .= '?key=' . $api_key;
6609 $headers = array(
6610 'Content-Type' => 'application/json'
6611 );
6612 } else {
6613 // OpenAI/Voyage use Bearer token
6614 $headers = array(
6615 'Authorization' => 'Bearer ' . $api_key,
6616 'Content-Type' => 'application/json'
6617 );
6618 }
6619
6620 // Make API request
6621 //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
6622 $response = wp_remote_post($endpoint, array(
6623 'body' => wp_json_encode($request_body),
6624 'headers' => $headers,
6625 'timeout' => 60 // Increased timeout for large inputs
6626 ));
6627
6628 // Handle wp_remote_post errors
6629 if (is_wp_error($response)) {
6630 $error_message = $response->get_error_message();
6631 //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
6632 return 'Connection error: ' . $error_message;
6633 }
6634
6635 // Get and check HTTP response code
6636 $http_code = wp_remote_retrieve_response_code($response);
6637 //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
6638
6639 if ($http_code !== 200) {
6640 $error_body = wp_remote_retrieve_body($response);
6641 //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
6642
6643 // Try to parse error for more details
6644 $error_json = json_decode($error_body, true);
6645 if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
6646 $error_type = $error_json['error']['type'] ?? 'unknown';
6647 $error_message = $error_json['error']['message'] ?? 'No message';
6648 //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
6649 //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
6650
6651 // Customize error message for common API errors
6652 if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
6653 $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
6654 } elseif ($error_type === 'authentication_error') {
6655 $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
6656 }
6657
6658 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
6659 return $error_message;
6660 }
6661
6662 $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
6663 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
6664 return $error_message;
6665 }
6666
6667 // Parse response body
6668 $response_body = wp_remote_retrieve_body($response);
6669 //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
6670
6671 $response_data = json_decode($response_body, true);
6672
6673 if (json_last_error() !== JSON_ERROR_NONE) {
6674 $error = json_last_error_msg();
6675 //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
6676 //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
6677 return "Failed to parse API response: $error";
6678 }
6679
6680 // Handle different response formats based on provider
6681 if (strpos($selected_model, 'gemini-embedding') === 0) {
6682 // Gemini API response format
6683 if (isset($response_data['embedding']['values'])) {
6684 $embedding_dimensions = count($response_data['embedding']['values']);
6685 //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
6686
6687 // Check if embedding dimensions are as expected (should be 1536)
6688 if ($embedding_dimensions !== 1536) {
6689 //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
6690 }
6691
6692 return $response_data['embedding']['values'];
6693 } else {
6694 //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
6695 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
6696
6697 if (isset($response_data['error'])) {
6698 $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
6699 //error_log('[MXCHAT-EMBED] ' . $error_message);
6700 return $error_message;
6701 }
6702
6703 $error_message = "Invalid Gemini API response format: No embedding found";
6704 //error_log('[MXCHAT-EMBED] ' . $error_message);
6705 return $error_message;
6706 }
6707 } else {
6708 // OpenAI/Voyage API response format
6709 if (isset($response_data['data'][0]['embedding'])) {
6710 $embedding_dimensions = count($response_data['data'][0]['embedding']);
6711 //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
6712
6713 // Check if embedding dimensions are as expected
6714 if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
6715 ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
6716 //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
6717 }
6718
6719 return $response_data['data'][0]['embedding'];
6720 } else {
6721 //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
6722 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
6723
6724 if (isset($response_data['error'])) {
6725 $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
6726 //error_log('[MXCHAT-EMBED] ' . $error_message);
6727 return $error_message;
6728 }
6729
6730 $error_message = "Invalid API response format: No embedding found";
6731 //error_log('[MXCHAT-EMBED] ' . $error_message);
6732 return $error_message;
6733 }
6734 }
6735 }
6736
6737
6738
6739 }
6740 ?>
6741