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

6,729 lines 308.4 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
3921
3922
3923
3924 public function mxchat_rate_limits_callback() {
3925 $all_options = get_option('mxchat_options', []);
3926
3927 // Define available rate limits
3928 $rate_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
3929
3930 // Define available timeframes
3931 $timeframes = array(
3932 'hourly' => __('Per Hour', 'mxchat'),
3933 'daily' => __('Per Day', 'mxchat'),
3934 'weekly' => __('Per Week', 'mxchat'),
3935 'monthly' => __('Per Month', 'mxchat')
3936 );
3937
3938 // Get all roles plus a "logged_out" pseudo-role
3939 $roles = wp_roles()->get_names();
3940 $roles['logged_out'] = __('Logged Out Users', 'mxchat');
3941
3942 // Start the wrapper
3943 echo '<div class="pro-feature-wrapper active">';
3944 echo '<div class="mxchat-rate-limits-container">';
3945
3946 echo '<p class="description" style="margin-bottom: 20px;">' .
3947 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') .
3948 '</p>';
3949
3950 // Output the controls for each role
3951 foreach ($roles as $role_id => $role_name) {
3952 // Get saved options or defaults
3953 $default_limit = ($role_id === 'logged_out') ? '10' : '100';
3954 $default_timeframe = 'daily';
3955 $default_message = __('Rate limit exceeded. Please try again later.', 'mxchat');
3956
3957 $selected_limit = isset($all_options['rate_limits'][$role_id]['limit'])
3958 ? $all_options['rate_limits'][$role_id]['limit']
3959 : $default_limit;
3960
3961 $selected_timeframe = isset($all_options['rate_limits'][$role_id]['timeframe'])
3962 ? $all_options['rate_limits'][$role_id]['timeframe']
3963 : $default_timeframe;
3964
3965 $custom_message = isset($all_options['rate_limits'][$role_id]['message'])
3966 ? $all_options['rate_limits'][$role_id]['message']
3967 : $default_message;
3968
3969 // Output the row
3970 echo '<div class="mxchat-rate-limit-row mxchat-autosave-section">';
3971
3972 // Role label
3973 echo '<div class="mxchat-rate-limit-role">' . esc_html($role_name) . '</div>';
3974
3975 // Controls section
3976 echo '<div class="mxchat-rate-limit-controls-wrapper">';
3977
3978 // Rate limit and timeframe controls
3979 echo '<div class="mxchat-rate-limit-controls">';
3980
3981 // Limit dropdown
3982 echo '<div>';
3983 echo '<label for="rate_limits_' . esc_attr($role_id) . '_limit">' . esc_html__('Limit:', 'mxchat') . '</label>';
3984 echo '<select
3985 id="rate_limits_' . esc_attr($role_id) . '_limit"
3986 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][limit]"
3987 class="mxchat-autosave-field">';
3988 foreach ($rate_limits as $limit) {
3989 echo '<option value="' . esc_attr($limit) . '" ' . selected($selected_limit, $limit, false) . '>' . esc_html($limit) . '</option>';
3990 }
3991 echo '</select>';
3992 echo '</div>';
3993
3994 // Timeframe dropdown
3995 echo '<div>';
3996 echo '<label for="rate_limits_' . esc_attr($role_id) . '_timeframe">' . esc_html__('Timeframe:', 'mxchat') . '</label>';
3997 echo '<select
3998 id="rate_limits_' . esc_attr($role_id) . '_timeframe"
3999 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][timeframe]"
4000 class="mxchat-autosave-field">';
4001 foreach ($timeframes as $value => $label) {
4002 echo '<option value="' . esc_attr($value) . '" ' . selected($selected_timeframe, $value, false) . '>' . esc_html($label) . '</option>';
4003 }
4004 echo '</select>';
4005 echo '</div>';
4006
4007 echo '</div>'; // End controls
4008
4009 // Custom message textarea
4010 echo '<div class="mxchat-rate-limit-message">';
4011 echo '<label for="rate_limits_' . esc_attr($role_id) . '_message">' . esc_html__('Custom Message:', 'mxchat') . '</label>';
4012 echo '<textarea
4013 id="rate_limits_' . esc_attr($role_id) . '_message"
4014 name="mxchat_options[rate_limits][' . esc_attr($role_id) . '][message]"
4015 class="mxchat-autosave-field"
4016 placeholder="' . esc_attr__('Enter custom message when rate limit is exceeded', 'mxchat') . '">' .
4017 esc_textarea($custom_message) .
4018 '</textarea>';
4019 echo '</div>'; // End message
4020
4021 echo '</div>'; // End controls wrapper
4022
4023 echo '</div>'; // End row
4024 }
4025
4026 echo '</div>'; // End container
4027
4028 echo '</div>'; // End pro-feature-wrapper
4029 }
4030 private function mxchat_add_option_field($id, $title, $callback = '') {
4031 add_settings_field(
4032 $id,
4033 __($title, 'mxchat'),
4034 $callback ? array($this, $callback) : array($this, $id . '_callback'),
4035 'mxchat-max',
4036 'mxchat_setting_section_id',
4037 $id === 'model' ? ['label_for' => 'model'] : []
4038 );
4039 }
4040
4041 // OpenAI API Key
4042 public function api_key_callback() {
4043 $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : '';
4044
4045 echo '<div class="api-key-wrapper" data-provider="openai">';
4046 echo '<input type="password" id="api_key" name="api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4047 echo '<button type="button" id="toggleApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4048 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4049 echo '</div>';
4050 }
4051
4052 // X.AI API Key
4053 public function xai_api_key_callback() {
4054 $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : '';
4055
4056 echo '<div class="api-key-wrapper" data-provider="xai">';
4057 echo '<input type="password" id="xai_api_key" name="xai_api_key" value="' . $xaiApiKey . '" class="regular-text" autocomplete="off" />';
4058 echo '<button type="button" id="toggleXaiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4059 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4060 echo '</div>';
4061 }
4062 // Claude API Key
4063 public function claude_api_key_callback() {
4064 $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : '';
4065
4066 echo '<div class="api-key-wrapper" data-provider="claude">';
4067 echo '<input type="password" id="claude_api_key" name="claude_api_key" value="' . $claudeApiKey . '" class="regular-text" autocomplete="off" />';
4068 echo '<button type="button" id="toggleClaudeApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4069 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4070 echo '</div>';
4071 }
4072
4073 // DeepSeek API Key
4074 public function deepseek_api_key_callback() {
4075 $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : '';
4076
4077 echo '<div class="api-key-wrapper" data-provider="deepseek">';
4078 echo '<input type="password" id="deepseek_api_key" name="deepseek_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4079 echo '<button type="button" id="toggleDeepSeekApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4080 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected chat model. Important: You must add credits before use.', 'mxchat') . '</p>';
4081 echo '</div>';
4082 }
4083
4084 // Gemini API Key
4085 public function gemini_api_key_callback() {
4086 $geminiApiKey = isset($this->options['gemini_api_key']) ? esc_attr($this->options['gemini_api_key']) : '';
4087
4088 echo '<div class="api-key-wrapper" data-provider="gemini">';
4089 echo '<input type="password" id="gemini_api_key" name="gemini_api_key" value="' . $geminiApiKey . '" class="regular-text" autocomplete="off" />';
4090 echo '<button type="button" id="toggleGeminiApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4091 echo '<p class="description api-key-notice">' . esc_html__('Required for Google Gemini models. Get your API key from Google AI Studio.', 'mxchat') . '</p>';
4092 echo '</div>';
4093 }
4094
4095 // Voyage API Key
4096 public function voyage_api_key_callback() {
4097 $apiKey = isset($this->options['voyage_api_key']) ? esc_attr($this->options['voyage_api_key']) : '';
4098
4099 echo '<div class="api-key-wrapper" data-provider="voyage">';
4100 echo '<input type="password" id="voyage_api_key" name="voyage_api_key" value="' . $apiKey . '" class="regular-text" autocomplete="off" />';
4101 echo '<button type="button" id="toggleVoyageAPIKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4102 echo '<p class="description api-key-notice">' . esc_html__('Required for your selected embedding model. Important: You must add credits before use.', 'mxchat') . '</p>';
4103 echo '</div>';
4104 }
4105
4106 public function mxchat_loops_api_key_callback() {
4107 $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : '';
4108
4109 // Hidden fields to "trap" autofill
4110 echo '<input type="text" style="display:none" autocomplete="username" />';
4111 echo '<input type="password" style="display:none" autocomplete="current-password" />';
4112
4113 echo '<div class="api-key-wrapper" data-provider="loops">';
4114 echo sprintf(
4115 '<input type="password" id="loops_api_key" name="loops_api_key" value="%s" class="regular-text" autocomplete="new-password" />',
4116 $loops_api_key
4117 );
4118 echo '<button type="button" id="toggleLoopsApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
4119 echo '</div>';
4120 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>';
4121 }
4122 public function mxchat_loops_mailing_list_callback() {
4123 // Add error handling and type checking
4124 $loops_api_key = '';
4125 $selected_list = '';
4126
4127 // Safely get the API key
4128 if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) {
4129 $loops_api_key = $this->options['loops_api_key'];
4130 }
4131
4132 // Safely get the selected list
4133 if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) {
4134 $selected_list = $this->options['loops_mailing_list'];
4135 }
4136
4137 if (!empty($loops_api_key)) {
4138 $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key);
4139 if (is_array($lists) && !empty($lists)) {
4140 echo '<select id="loops_mailing_list" name="loops_mailing_list">';
4141
4142 // Add a default "Select a list" option
4143 echo '<option value="" ' . selected($selected_list, '', false) . '>' . esc_html__('Select a list', 'mxchat') . '</option>';
4144
4145 foreach ($lists as $list) {
4146 if (is_array($list) && isset($list['id']) && isset($list['name'])) {
4147 echo sprintf(
4148 '<option value="%s" %s>%s</option>',
4149 esc_attr($list['id']),
4150 selected($selected_list, $list['id'], false),
4151 esc_html($list['name'])
4152 );
4153 }
4154 }
4155 echo '</select>';
4156 echo '<p class="description">' . esc_html__('Please select a mailing list to use with Loops.', 'mxchat') . '</p>';
4157 } else {
4158 echo '<p class="description">' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '</p>';
4159 }
4160 } else {
4161 echo '<p class="description">' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '</p>';
4162 }
4163 }
4164 public function mxchat_triggered_phrase_response_callback() {
4165 $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat');
4166 $triggered_response = isset($this->options['triggered_phrase_response'])
4167 ? $this->options['triggered_phrase_response']
4168 : $default_response;
4169
4170 echo sprintf(
4171 '<textarea id="triggered_phrase_response" name="triggered_phrase_response" rows="3" cols="50">%s</textarea>',
4172 esc_textarea($triggered_response)
4173 );
4174 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>';
4175 }
4176
4177 public function mxchat_email_capture_response_callback() {
4178 $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat');
4179 $email_capture_response = isset($this->options['email_capture_response'])
4180 ? $this->options['email_capture_response']
4181 : $default_response;
4182
4183 echo sprintf(
4184 '<textarea id="email_capture_response" name="email_capture_response" rows="3" cols="50">%s</textarea>',
4185 esc_textarea($email_capture_response)
4186 );
4187 echo '<p class="description">' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '</p>';
4188 }
4189
4190 public function mxchat_pre_chat_message_callback() {
4191 // Load the entire 'mxchat_options' array
4192 $all_options = get_option('mxchat_options', []);
4193
4194 // Retrieve the saved message or use the default value
4195 $default_message = __('Hey there! Ask me anything!', 'mxchat');
4196 $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message;
4197
4198 // Output the textarea
4199 printf(
4200 '<textarea id="pre_chat_message" name="pre_chat_message" rows="5" cols="50">%s</textarea>',
4201 esc_textarea($pre_chat_message)
4202 );
4203 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>';
4204 }
4205
4206 // Callback for AI Instructions textarea
4207 public function system_prompt_instructions_callback() {
4208 // Retrieve the current value of the system prompt instructions
4209 $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : '';
4210 // Render the textarea field
4211 printf(
4212 '<textarea id="system_prompt_instructions" name="system_prompt_instructions" rows="5" cols="50">%s</textarea>',
4213 $instructions
4214 );
4215 // Provide a helpful description with sample instructions button
4216 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>';
4217 echo '<div class="mxchat-instructions-container">';
4218 echo '<button type="button" class="mxchat-instructions-btn" id="mxchatViewSampleBtn">';
4219 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">';
4220 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4221 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4222 echo '</svg>';
4223 echo esc_html__('View Sample Instructions', 'mxchat');
4224 echo '</button>';
4225 echo '</div>';
4226
4227 // Add modal to WordPress admin footer instead of inline
4228 add_action('admin_footer', array($this, 'render_sample_instructions_modal'));
4229 }
4230
4231 // New method to render modal in admin footer
4232 public function render_sample_instructions_modal() {
4233 static $modal_rendered = false;
4234 if ($modal_rendered) return; // Prevent duplicate modals
4235 $modal_rendered = true;
4236
4237 echo '<div class="mxchat-instructions-modal-overlay" id="mxchatSampleModal">';
4238 echo '<div class="mxchat-instructions-modal-content">';
4239 echo '<div class="mxchat-instructions-modal-header">';
4240 echo '<h3 class="mxchat-instructions-modal-title">';
4241 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">';
4242 echo '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>';
4243 echo '<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>';
4244 echo '</svg>';
4245 echo esc_html__('Sample AI Instructions', 'mxchat');
4246 echo '</h3>';
4247 echo '<button type="button" class="mxchat-instructions-modal-close" id="mxchatModalClose">';
4248 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">';
4249 echo '<line x1="18" y1="6" x2="6" y2="18"/>';
4250 echo '<line x1="6" y1="6" x2="18" y2="18"/>';
4251 echo '</svg>';
4252 echo '</button>';
4253 echo '</div>';
4254 echo '<div class="mxchat-instructions-modal-body">';
4255 echo '<div class="mxchat-instructions-content">';
4256 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:
4257
4258 # Response Style - CRITICALLY IMPORTANT
4259 - MAXIMUM LENGTH: 1-3 short sentences per response
4260 - Ultra-concise: Get straight to the answer with no filler
4261 - No introductions like "Sure!" or "I\'d be happy to help"
4262 - No phrases like "based on my knowledge" or "according to information"
4263 - No explanatory text before giving the answer
4264 - No summaries or repetition
4265 - Hyperlink all URLs
4266 - Respond in user\'s language
4267 - Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
4268
4269 # Knowledge Base Requirements - PREVENT HALLUCINATIONS
4270 - ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
4271 - If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
4272 - NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
4273 - When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
4274 - Better to admit insufficient information than provide inaccurate answers');
4275 echo '</div>';
4276 echo '<button type="button" class="mxchat-instructions-copy-btn" id="mxchatCopyBtn">';
4277 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">';
4278 echo '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>';
4279 echo '<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>';
4280 echo '</svg>';
4281 echo esc_html__('Copy Instructions', 'mxchat');
4282 echo '</button>';
4283 echo '</div>';
4284 echo '<div class="mxchat-instructions-modal-footer">';
4285 echo '<button type="button" class="mxchat-instructions-btn-secondary" id="mxchatCloseBtn">' . esc_html__('Close', 'mxchat') . '</button>';
4286 echo '</div>';
4287 echo '</div>';
4288 echo '</div>';
4289 }
4290
4291
4292 public function mxchat_model_callback() {
4293 // Define available models grouped by provider
4294 $models = array(
4295 esc_html__('Google Gemini Models', 'mxchat') => array(
4296 'gemini-2.0-flash' => esc_html__('Gemini 2.0 Flash (Next-Gen Features)', 'mxchat'),
4297 'gemini-2.0-flash-lite' => esc_html__('Gemini 2.0 Flash-Lite (Cost-Efficient)', 'mxchat'),
4298 'gemini-1.5-pro' => esc_html__('Gemini 1.5 Pro (Complex Reasoning)', 'mxchat'),
4299 'gemini-1.5-flash' => esc_html__('Gemini 1.5 Flash (Fast & Versatile)', 'mxchat'),
4300 ),
4301 esc_html__('X.AI Models', 'mxchat') => array(
4302 'grok-3-beta' => esc_html__('Grok-3 (Powerful)', 'mxchat'),
4303 'grok-3-fast-beta' => esc_html__('Grok-3 Fast (High Performance)', 'mxchat'),
4304 'grok-3-mini-beta' => esc_html__('Grok-3 Mini (Affordable)', 'mxchat'),
4305 'grok-3-mini-fast-beta' => esc_html__('Grok-3 Mini Fast (Quick Response)', 'mxchat'),
4306 'grok-2' => esc_html__('Grok 2', 'mxchat')
4307 ),
4308 esc_html__('DeepSeek Models', 'mxchat') => array(
4309 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'),
4310 ),
4311 esc_html__('Claude Models', 'mxchat') => array(
4312 'claude-opus-4-20250514' => esc_html__('Claude 4 Opus (Most Capable)', 'mxchat'),
4313 'claude-sonnet-4-20250514' => esc_html__('Claude 4 Sonnet (High Performance)', 'mxchat'),
4314 'claude-3-7-sonnet-20250219' => esc_html__('Claude 3.7 Sonnet (High Intelligence)', 'mxchat'),
4315 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Intelligent)', 'mxchat'),
4316 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Complex Tasks)', 'mxchat'),
4317 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'),
4318 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat')
4319 ),
4320 esc_html__('OpenAI Models', 'mxchat') => array(
4321 'gpt-4.1-2025-04-14' => esc_html__('GPT-4.1 (Flagship for Complex Tasks)', 'mxchat'),
4322 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'),
4323 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'),
4324 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'),
4325 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'),
4326 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat')
4327 )
4328 );
4329
4330 // Retrieve the currently selected model from saved options
4331 $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o';
4332
4333 // Begin the select dropdown
4334 echo '<select id="model" name="model">';
4335
4336 // Iterate over groups of models
4337 foreach ($models as $group_label => $group_models) {
4338 echo '<optgroup label="' . esc_attr($group_label) . '">';
4339
4340 foreach ($group_models as $model_value => $model_label) {
4341 // All models enabled - no disabled attribute or Pro Only label
4342 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4343 }
4344
4345 echo '</optgroup>';
4346 }
4347
4348 // Close the select dropdown
4349 echo '</select>';
4350
4351 // Updated description to remove mention of Pro-only models
4352 echo '<p class="description">' . esc_html__('Select the AI model your chatbot will use for chatting.', 'mxchat') . '</p>';
4353 }
4354
4355 public function enable_streaming_toggle_callback() {
4356 // Get value from options array, default to 'on'
4357 $enabled = isset($this->options['enable_streaming_toggle']) ? $this->options['enable_streaming_toggle'] : 'on';
4358 $checked = ($enabled === 'on') ? 'checked' : '';
4359
4360 echo '<label class="toggle-switch">';
4361 echo sprintf(
4362 '<input type="checkbox" id="enable_streaming_toggle" name="enable_streaming_toggle" value="on" %s />',
4363 esc_attr($checked)
4364 );
4365 echo '<span class="slider"></span>'; // Remove 'round' class to match working toggle
4366 echo '</label>';
4367 echo '<p class="description">' .
4368 esc_html__('Enable real-time streaming responses for supported models (OpenAI and Claude). When disabled, responses will load all at once.', 'mxchat') .
4369 '</p>';
4370 }
4371 // Callback function for embedding model selection
4372 public function embedding_model_callback() {
4373 $models = array(
4374 esc_html__('OpenAI Embeddings', 'mxchat') => array(
4375 'text-embedding-3-small' => esc_html__('TE3 Small (1536, Efficient)', 'mxchat'),
4376 'text-embedding-ada-002' => esc_html__('Ada 2 (1536, Recommended)', 'mxchat'),
4377 'text-embedding-3-large' => esc_html__('TE3 Large (3072, Powerful)', 'mxchat'),
4378 ),
4379 esc_html__('Voyage AI Embeddings', 'mxchat') => array(
4380 'voyage-3-large' => esc_html__('Voyage-3 Large (2048, Most Capable)', 'mxchat'),
4381 ),
4382 esc_html__('Google Gemini Embeddings', 'mxchat') => array(
4383 'gemini-embedding-exp-03-07' => esc_html__('Gemini Embedding (1536, Experimental)', 'mxchat'),
4384 )
4385 );
4386 $selected_model = isset($this->options['embedding_model']) ? esc_attr($this->options['embedding_model']) : 'text-embedding-ada-002';
4387 echo '<select id="embedding_model" name="embedding_model">';
4388 foreach ($models as $group_label => $group_models) {
4389 echo '<optgroup label="' . esc_attr($group_label) . '">';
4390 foreach ($group_models as $model_value => $model_label) {
4391 echo '<option value="' . esc_attr($model_value) . '" ' . selected($selected_model, $model_value, false) . '>' . esc_html($model_label) . '</option>';
4392 }
4393 echo '</optgroup>';
4394 }
4395 echo '</select>';
4396 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>';
4397 }
4398
4399
4400 public function mxchat_top_bar_title_callback() {
4401 // Retrieve the current value of the top bar title from saved options
4402 $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : '';
4403
4404 // Render the input field
4405 echo '<input type="text" id="top_bar_title" name="top_bar_title" value="' . $top_bar_title . '" />';
4406
4407 // Add a description
4408 echo '<p class="description">' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '</p>';
4409 }
4410 public function mxchat_ai_agent_text_callback() {
4411 // Retrieve the current value of the AI agent text from saved options
4412 $ai_agent_text = isset($this->options['ai_agent_text']) ? esc_attr($this->options['ai_agent_text']) : '';
4413 // Render the input field
4414 echo '<input type="text" id="ai_agent_text" name="ai_agent_text" value="' . $ai_agent_text . '" />';
4415 // Add a description
4416 echo '<p class="description">' . esc_html__('Enter the text that will appear for AI agents in the status indicator. Default: "AI Agent"', 'mxchat') . '</p>';
4417 }
4418 public function enable_email_block_callback() {
4419 // Load full plugin options array
4420 $all_options = get_option('mxchat_options', []);
4421
4422 // Get the value, default to 'off'
4423 $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off';
4424
4425 // Check if it's 'on'
4426 $checked = ($enable_email_block === 'on') ? 'checked' : '';
4427
4428 echo '<label class="toggle-switch">';
4429 echo sprintf(
4430 '<input type="checkbox" id="enable_email_block" name="enable_email_block" value="on" %s />',
4431 esc_attr($checked)
4432 );
4433 echo '<span class="slider"></span>';
4434 echo '</label>';
4435 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>';
4436 }
4437
4438
4439 public function email_blocker_header_content_callback() {
4440 // Load the entire 'mxchat_options' array
4441 $all_options = get_option('mxchat_options', []);
4442
4443 // Retrieve the saved content or default to empty
4444 $content = isset($all_options['email_blocker_header_content'])
4445 ? $all_options['email_blocker_header_content']
4446 : '';
4447
4448 // Render the textarea - IMPORTANT: name should be just "email_blocker_header_content"
4449 echo '<textarea
4450 id="email_blocker_header_content"
4451 name="email_blocker_header_content"
4452 rows="5"
4453 cols="70"
4454 data-setting="email_blocker_header_content"
4455 >' . esc_textarea($content) . '</textarea>';
4456
4457 echo '<p class="description">';
4458 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');
4459 echo '</p>';
4460 }
4461
4462 public function email_blocker_button_text_callback() {
4463 // Load the entire 'mxchat_options' array
4464 $all_options = get_option('mxchat_options', []);
4465
4466 // Retrieve the saved button text or default to empty
4467 $button_text = isset($all_options['email_blocker_button_text'])
4468 ? $all_options['email_blocker_button_text']
4469 : '';
4470
4471 // Use esc_attr to safely render the existing text
4472 echo '<input type="text" id="email_blocker_button_text" name="email_blocker_button_text" value="' . esc_attr($button_text) . '" style="width: 300px;" />';
4473
4474 echo '<p class="description">';
4475 echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat');
4476 echo '</p>';
4477 }
4478
4479
4480
4481 public function mxchat_intro_message_callback() {
4482 // Load the entire 'mxchat_options' array
4483 $all_options = get_option('mxchat_options', []);
4484 // Retrieve the saved intro message or use the default
4485 $default_message = __('Hello! How can I assist you today?', 'mxchat');
4486 $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message;
4487 // Output the textarea with the saved value without escaping HTML
4488 ?>
4489 <textarea id="intro_message" name="intro_message" rows="5" cols="50"><?php echo $saved_message; ?></textarea>
4490 <p class="description">
4491 <?php esc_html_e('Enter your message. HTML tags and line breaks will be preserved.', 'mxchat'); ?>
4492 </p>
4493 <?php
4494 }
4495
4496 public function mxchat_input_copy_callback() {
4497 // Load the entire 'mxchat_options' array
4498 $all_options = get_option('mxchat_options', []);
4499
4500 // Retrieve the saved input copy or use the default value
4501 $default_copy = __('How can I assist?', 'mxchat');
4502 $input_copy = isset($all_options['input_copy']) ? $all_options['input_copy'] : $default_copy;
4503
4504 // Output the input field with the saved value
4505 printf(
4506 '<input type="text" id="input_copy" name="input_copy" value="%s" placeholder="%s" />',
4507 esc_attr($input_copy),
4508 esc_attr__('How can I assist?', 'mxchat')
4509 );
4510
4511 // Output the description
4512 echo '<p class="description">' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '</p>';
4513 }
4514
4515
4516
4517 public function mxchat_user_message_font_color_callback() {
4518 $disabled = $this->is_activated ? '' : 'disabled';
4519 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4520
4521 echo '<div class="' . esc_attr($class) . '">';
4522 echo sprintf(
4523 '<input type="text"
4524 id="user_message_font_color"
4525 name="user_message_font_color"
4526 value="%s"
4527 class="my-color-field"
4528 data-default-color="#ffffff"
4529 %s />',
4530 isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff',
4531 esc_attr($disabled)
4532 );
4533
4534 if (!$this->is_activated) {
4535 echo '<div class="pro-feature-overlay">';
4536 echo '<a href="https://mxchat.ai/" target="_blank">';
4537 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4538 echo '</a>';
4539 echo '</div>';
4540 }
4541 echo '</div>';
4542 }
4543
4544 public function mxchat_bot_message_bg_color_callback() {
4545 $disabled = $this->is_activated ? '' : 'disabled';
4546 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4547
4548 echo '<div class="' . esc_attr($class) . '">';
4549 echo sprintf(
4550 '<input type="text"
4551 id="bot_message_bg_color"
4552 name="bot_message_bg_color"
4553 value="%s"
4554 class="my-color-field"
4555 data-default-color="#e1e1e1"
4556 %s />',
4557 isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1',
4558 esc_attr($disabled)
4559 );
4560
4561 if (!$this->is_activated) {
4562 echo '<div class="pro-feature-overlay">';
4563 echo '<a href="https://mxchat.ai/" target="_blank">';
4564 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4565 echo '</a>';
4566 echo '</div>';
4567 }
4568 echo '</div>';
4569 }
4570
4571 public function mxchat_bot_message_font_color_callback() {
4572 $disabled = $this->is_activated ? '' : 'disabled';
4573 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4574
4575 echo '<div class="' . esc_attr($class) . '">';
4576 echo sprintf(
4577 '<input type="text"
4578 id="bot_message_font_color"
4579 name="bot_message_font_color"
4580 value="%s"
4581 class="my-color-field"
4582 data-default-color="#333333"
4583 %s />',
4584 isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333',
4585 esc_attr($disabled)
4586 );
4587
4588 if (!$this->is_activated) {
4589 echo '<div class="pro-feature-overlay">';
4590 echo '<a href="https://mxchat.ai/" target="_blank">';
4591 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4592 echo '</a>';
4593 echo '</div>';
4594 }
4595 echo '</div>';
4596 }
4597
4598 public function mxchat_live_agent_message_bg_color_callback() {
4599 $disabled = $this->is_activated ? '' : 'disabled';
4600 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4601
4602 echo '<div class="' . esc_attr($class) . '">';
4603 echo sprintf(
4604 '<input type="text"
4605 id="live_agent_message_bg_color"
4606 name="live_agent_message_bg_color"
4607 value="%s"
4608 class="my-color-field"
4609 data-default-color="#ffffff"
4610 %s />',
4611 isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff',
4612 esc_attr($disabled)
4613 );
4614
4615 if (!$this->is_activated) {
4616 echo '<div class="pro-feature-overlay">';
4617 echo '<a href="https://mxchat.ai/" target="_blank">';
4618 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4619 echo '</a>';
4620 echo '</div>';
4621 }
4622 echo '</div>';
4623 }
4624
4625 public function mxchat_live_agent_message_font_color_callback() {
4626 $disabled = $this->is_activated ? '' : 'disabled';
4627 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4628
4629 echo '<div class="' . esc_attr($class) . '">';
4630 echo sprintf(
4631 '<input type="text"
4632 id="live_agent_message_font_color"
4633 name="live_agent_message_font_color"
4634 value="%s"
4635 class="my-color-field"
4636 data-default-color="#333333"
4637 %s />',
4638 isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333',
4639 esc_attr($disabled)
4640 );
4641
4642 if (!$this->is_activated) {
4643 echo '<div class="pro-feature-overlay">';
4644 echo '<a href="https://mxchat.ai/" target="_blank">';
4645 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4646 echo '</a>';
4647 echo '</div>';
4648 }
4649 echo '</div>';
4650 }
4651
4652 public function mxchat_mode_indicator_bg_color_callback() {
4653 $disabled = $this->is_activated ? '' : 'disabled';
4654 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4655
4656 echo '<div class="' . esc_attr($class) . '">';
4657 echo sprintf(
4658 '<input type="text"
4659 id="mode_indicator_bg_color"
4660 name="mode_indicator_bg_color"
4661 value="%s"
4662 class="my-color-field"
4663 data-default-color="#767676"
4664 %s />',
4665 isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676',
4666 esc_attr($disabled)
4667 );
4668
4669 if (!$this->is_activated) {
4670 echo '<div class="pro-feature-overlay">';
4671 echo '<a href="https://mxchat.ai/" target="_blank">';
4672 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4673 echo '</a>';
4674 echo '</div>';
4675 }
4676 echo '</div>';
4677 }
4678
4679 public function mxchat_mode_indicator_font_color_callback() {
4680 $disabled = $this->is_activated ? '' : 'disabled';
4681 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4682
4683 echo '<div class="' . esc_attr($class) . '">';
4684 echo sprintf(
4685 '<input type="text"
4686 id="mode_indicator_font_color"
4687 name="mode_indicator_font_color"
4688 value="%s"
4689 class="my-color-field"
4690 data-default-color="#ffffff"
4691 %s />',
4692 isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff',
4693 esc_attr($disabled)
4694 );
4695
4696 if (!$this->is_activated) {
4697 echo '<div class="pro-feature-overlay">';
4698 echo '<a href="https://mxchat.ai/" target="_blank">';
4699 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4700 echo '</a>';
4701 echo '</div>';
4702 }
4703 echo '</div>';
4704 }
4705
4706 public function mxchat_toolbar_icon_color_callback() {
4707 $disabled = $this->is_activated ? '' : 'disabled';
4708 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4709
4710 echo '<div class="' . esc_attr($class) . '">';
4711 echo sprintf(
4712 '<input type="text"
4713 id="toolbar_icon_color"
4714 name="toolbar_icon_color"
4715 value="%s"
4716 class="my-color-field"
4717 data-default-color="#212121"
4718 %s />',
4719 isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121',
4720 esc_attr($disabled)
4721 );
4722
4723 if (!$this->is_activated) {
4724 echo '<div class="pro-feature-overlay">';
4725 echo '<a href="https://mxchat.ai/" target="_blank">';
4726 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4727 echo '</a>';
4728 echo '</div>';
4729 }
4730 echo '</div>';
4731 }
4732
4733 public function mxchat_top_bar_bg_color_callback() {
4734 $disabled = $this->is_activated ? '' : 'disabled';
4735 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4736
4737 echo '<div class="' . esc_attr($class) . '">';
4738 echo sprintf(
4739 '<input type="text"
4740 id="top_bar_bg_color"
4741 name="top_bar_bg_color"
4742 value="%s"
4743 class="my-color-field"
4744 data-default-color="#00b294"
4745 %s />',
4746 isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294',
4747 esc_attr($disabled)
4748 );
4749
4750 if (!$this->is_activated) {
4751 echo '<div class="pro-feature-overlay">';
4752 echo '<a href="https://mxchat.ai/" target="_blank">';
4753 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4754 echo '</a>';
4755 echo '</div>';
4756 }
4757 echo '</div>';
4758 }
4759
4760 public function mxchat_send_button_font_color_callback() {
4761 $disabled = $this->is_activated ? '' : 'disabled';
4762 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4763
4764 echo '<div class="' . esc_attr($class) . '">';
4765 echo sprintf(
4766 '<input type="text"
4767 id="send_button_font_color"
4768 name="send_button_font_color"
4769 value="%s"
4770 class="my-color-field"
4771 data-default-color="#ffffff"
4772 %s />',
4773 isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff',
4774 esc_attr($disabled)
4775 );
4776
4777 if (!$this->is_activated) {
4778 echo '<div class="pro-feature-overlay">';
4779 echo '<a href="https://mxchat.ai/" target="_blank">';
4780 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4781 echo '</a>';
4782 echo '</div>';
4783 }
4784 echo '</div>';
4785 }
4786
4787 public function mxchat_chatbot_background_color_callback() {
4788 $disabled = $this->is_activated ? '' : 'disabled';
4789 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4790
4791 echo '<div class="' . esc_attr($class) . '">';
4792 echo sprintf(
4793 '<input type="text"
4794 id="chatbot_background_color"
4795 name="chatbot_background_color"
4796 value="%s"
4797 class="my-color-field"
4798 data-default-color="#000000"
4799 %s />',
4800 isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000',
4801 esc_attr($disabled)
4802 );
4803
4804 if (!$this->is_activated) {
4805 echo '<div class="pro-feature-overlay">';
4806 echo '<a href="https://mxchat.ai/" target="_blank">';
4807 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4808 echo '</a>';
4809 echo '</div>';
4810 }
4811 echo '</div>';
4812 }
4813
4814 public function mxchat_icon_color_callback() {
4815 $disabled = $this->is_activated ? '' : 'disabled';
4816 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4817
4818 echo '<div class="' . esc_attr($class) . '">';
4819 echo sprintf(
4820 '<input type="text"
4821 id="icon_color"
4822 name="icon_color"
4823 value="%s"
4824 class="my-color-field"
4825 data-default-color="#ffffff"
4826 %s />',
4827 isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff',
4828 esc_attr($disabled)
4829 );
4830
4831 if (!$this->is_activated) {
4832 echo '<div class="pro-feature-overlay">';
4833 echo '<a href="https://mxchat.ai/" target="_blank">';
4834 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4835 echo '</a>';
4836 echo '</div>';
4837 }
4838 echo '</div>';
4839 }
4840
4841 public function mxchat_custom_icon_callback() {
4842 $disabled = $this->is_activated ? '' : 'disabled';
4843 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4844 $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : '';
4845
4846 echo '<div class="' . esc_attr($class) . '">';
4847 echo sprintf(
4848 '<input type="url"
4849 id="custom_icon"
4850 name="custom_icon"
4851 value="%s"
4852 placeholder="%s"
4853 class="regular-text"
4854 %s />',
4855 $custom_icon_url,
4856 esc_attr__('Enter PNG URL', 'mxchat'),
4857 esc_attr($disabled)
4858 );
4859
4860 // Preview container for the icon
4861 if (!empty($custom_icon_url)) {
4862 echo '<div class="icon-preview" style="margin-top: 10px;">';
4863 echo '<img src="' . esc_url($custom_icon_url) . '" alt="' . esc_attr__('Custom Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4864 echo '</div>';
4865 }
4866
4867 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4868
4869 if (!$this->is_activated) {
4870 echo '<div class="pro-feature-overlay">';
4871 echo '<a href="https://mxchat.ai/" target="_blank">';
4872 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4873 echo '</a>';
4874 echo '</div>';
4875 }
4876 echo '</div>';
4877 }
4878
4879 public function mxchat_title_icon_callback() {
4880 $disabled = $this->is_activated ? '' : 'disabled';
4881 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4882 // Fixed the variable reference - it was using custom_icon instead of title_icon
4883 $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : '';
4884
4885 echo '<div class="' . esc_attr($class) . '">';
4886 echo sprintf(
4887 '<input type="url"
4888 id="title_icon"
4889 name="title_icon"
4890 value="%s"
4891 placeholder="%s"
4892 class="regular-text"
4893 %s />',
4894 $title_icon_url,
4895 esc_attr__('Enter PNG URL', 'mxchat'),
4896 esc_attr($disabled)
4897 );
4898
4899 // Preview container for the icon
4900 if (!empty($title_icon_url)) {
4901 echo '<div class="icon-preview" style="margin-top: 10px;">';
4902 echo '<img src="' . esc_url($title_icon_url) . '" alt="' . esc_attr__('Title Icon Preview', 'mxchat') . '" style="max-width: 48px; height: auto;" />';
4903 echo '</div>';
4904 }
4905
4906 echo '<p class="description">' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '</p>';
4907
4908 if (!$this->is_activated) {
4909 echo '<div class="pro-feature-overlay">';
4910 echo '<a href="https://mxchat.ai/" target="_blank">';
4911 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4912 echo '</a>';
4913 echo '</div>';
4914 }
4915 echo '</div>';
4916 }
4917
4918 public function mxchat_chat_input_font_color_callback() {
4919 $disabled = $this->is_activated ? '' : 'disabled';
4920 $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive';
4921
4922 echo '<div class="' . esc_attr($class) . '">';
4923 echo sprintf(
4924 '<input type="text"
4925 id="chat_input_font_color"
4926 name="chat_input_font_color"
4927 value="%s"
4928 class="my-color-field"
4929 data-default-color="#555555"
4930 %s />',
4931 isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555',
4932 esc_attr($disabled)
4933 );
4934
4935 if (!$this->is_activated) {
4936 echo '<div class="pro-feature-overlay">';
4937 echo '<a href="https://mxchat.ai/" target="_blank">';
4938 echo '<img src="' . esc_url(plugin_dir_url(__FILE__) . '../images/pro-only-dark.png') . '" alt="' . esc_attr__('Pro Only', 'mxchat') . '" />';
4939 echo '</a>';
4940 echo '</div>';
4941 }
4942 echo '</div>';
4943 }
4944
4945 public function mxchat_append_to_body_callback() {
4946 // Get value from options array, default to 'off'
4947 $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off';
4948 $checked = ($append_to_body === 'on') ? 'checked' : '';
4949
4950 echo '<label class="toggle-switch">';
4951 echo sprintf(
4952 '<input type="checkbox" id="append_to_body" name="append_to_body" value="on" %s />',
4953 esc_attr($checked)
4954 );
4955 echo '<span class="slider"></span>';
4956 echo '</label>';
4957 echo '<p class="description">' .
4958 esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') .
4959 '</p>';
4960
4961 }
4962
4963
4964
4965 public function mxchat_privacy_toggle_callback() {
4966 // Load from mxchat_options array
4967 $options = get_option('mxchat_options', []);
4968
4969 // Get privacy toggle value with fallback
4970 $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off';
4971 $checked = ($privacy_toggle === 'on') ? 'checked' : '';
4972
4973 // Get privacy text with fallback
4974 $privacy_text = isset($options['privacy_text'])
4975 ? $options['privacy_text']
4976 : __('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat');
4977
4978 // Output the toggle switch
4979 echo '<label class="toggle-switch">';
4980 echo sprintf(
4981 '<input type="checkbox" id="privacy_toggle" name="privacy_toggle" value="on" %s />',
4982 esc_attr($checked)
4983 );
4984 echo '<span class="slider"></span>';
4985 echo '</label>';
4986 echo '<p class="description">' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '</p>';
4987
4988 // Output the custom text input field
4989 echo sprintf(
4990 '<textarea id="privacy_text" name="privacy_text" rows="5" cols="50" class="regular-text">%s</textarea>',
4991 esc_textarea($privacy_text)
4992 );
4993 echo '<p class="description">' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '</p>';
4994 }
4995
4996
4997 public function mxchat_complianz_toggle_callback() {
4998 // Load from mxchat_options array
4999 $options = get_option('mxchat_options', []);
5000
5001 // Get complianz toggle value with fallback
5002 $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off';
5003 $checked = ($complianz_toggle === 'on') ? 'checked' : '';
5004
5005 // Output the toggle switch
5006 echo '<label class="toggle-switch">';
5007 echo sprintf(
5008 '<input type="checkbox" id="complianz_toggle" name="complianz_toggle" value="on" %s />',
5009 esc_attr($checked)
5010 );
5011 echo '<span class="slider"></span>';
5012 echo '</label>';
5013
5014 echo '<p class="description">' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '</p>';
5015 }
5016
5017 public function mxchat_link_target_toggle_callback() {
5018 // Load from mxchat_options array
5019 $options = get_option('mxchat_options', []);
5020
5021 // Get link target toggle value with fallback
5022 $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off';
5023 $checked = ($link_target_toggle === 'on') ? 'checked' : '';
5024
5025 // Output the toggle switch
5026 echo '<label class="toggle-switch">';
5027 echo sprintf(
5028 '<input type="checkbox" id="link_target_toggle" name="link_target_toggle" value="on" %s />',
5029 esc_attr($checked)
5030 );
5031 echo '<span class="slider"></span>';
5032 echo '</label>';
5033 echo '<p class="description">' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '</p>';
5034 }
5035
5036 public function mxchat_chat_persistence_toggle_callback() {
5037 // Load from mxchat_options array
5038 $options = get_option('mxchat_options', []);
5039
5040 // Get chat persistence toggle value with fallback
5041 $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off';
5042 $checked = ($chat_persistence_toggle === 'on') ? 'checked' : '';
5043
5044 // Output the toggle switch
5045 echo '<label class="toggle-switch">';
5046 echo sprintf(
5047 '<input type="checkbox" id="chat_persistence_toggle" name="chat_persistence_toggle" value="on" %s />',
5048 esc_attr($checked)
5049 );
5050 echo '<span class="slider"></span>';
5051 echo '</label>';
5052
5053 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>';
5054 }
5055
5056 public function mxchat_popular_question_1_callback() {
5057 // Load the full plugin options array
5058 $all_options = get_option('mxchat_options', []);
5059
5060 // Retrieve the specific option for popular_question_1
5061 $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : '';
5062
5063 // Render the input field
5064 printf(
5065 '<input type="text" id="popular_question_1" name="popular_question_1" value="%s" placeholder="%s" class="regular-text" />',
5066 esc_attr($popular_question_1),
5067 esc_attr__('Enter Quick Question 1', 'mxchat')
5068 );
5069
5070 // Add a description for the field
5071 echo '<p class="description">' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '</p>';
5072 }
5073
5074
5075 public function mxchat_popular_question_2_callback() {
5076 // Load the full plugin options array
5077 $all_options = get_option('mxchat_options', []);
5078
5079 // Retrieve the specific option for popular_question_2
5080 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : '';
5081
5082 // Render the input field
5083 printf(
5084 '<input type="text" id="popular_question_2" name="popular_question_2" value="%s" placeholder="%s" class="regular-text" />',
5085 esc_attr($popular_question_2),
5086 esc_attr__('Enter Quick Question 2', 'mxchat')
5087 );
5088
5089 // Add a description for the field
5090 echo '<p class="description">' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '</p>';
5091 }
5092
5093
5094 public function mxchat_popular_question_3_callback() {
5095 // Load the full plugin options array
5096 $all_options = get_option('mxchat_options', []);
5097
5098 // Retrieve the specific option for popular_question_3
5099 $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : '';
5100
5101 // Render the input field
5102 printf(
5103 '<input type="text" id="popular_question_3" name="popular_question_3" value="%s" placeholder="%s" class="regular-text" />',
5104 esc_attr($popular_question_3),
5105 esc_attr(__('Enter Quick Question 3', 'mxchat'))
5106 );
5107
5108 // Add a description for the field
5109 echo '<p class="description">' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '</p>';
5110 }
5111
5112 public function mxchat_additional_popular_questions_callback() {
5113 $options = get_option('mxchat_options', []);
5114 $additional_questions = isset($options['additional_popular_questions'])
5115 ? $options['additional_popular_questions']
5116 : get_option('additional_popular_questions', array());
5117
5118 echo '<div id="mxchat-additional-questions-container">';
5119 if (!empty($additional_questions)) {
5120 foreach ($additional_questions as $index => $question) {
5121 printf(
5122 '<div class="mxchat-question-row">
5123 <input type="text" name="additional_popular_questions[]"
5124 value="%s"
5125 placeholder="%s"
5126 class="regular-text mxchat-question-input"
5127 data-question-index="%d" />
5128 <button type="button" class="button mxchat-remove-question"
5129 aria-label="%s">%s</button>
5130 </div>',
5131 esc_attr($question),
5132 esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)),
5133 $index,
5134 esc_attr(__('Remove question', 'mxchat')),
5135 esc_html__('Remove', 'mxchat')
5136 );
5137 }
5138 } else {
5139 printf(
5140 '<div class="mxchat-question-row">
5141 <input type="text" name="additional_popular_questions[]"
5142 value=""
5143 placeholder="%s"
5144 class="regular-text mxchat-question-input"
5145 data-question-index="0" />
5146 <button type="button" class="button mxchat-remove-question"
5147 aria-label="%s">%s</button>
5148 </div>',
5149 esc_attr(__('Enter Additional Quick Question 4', 'mxchat')),
5150 esc_attr(__('Remove question', 'mxchat')),
5151 esc_html__('Remove', 'mxchat')
5152 );
5153 }
5154 echo '</div>';
5155 printf(
5156 '<button type="button" class="button mxchat-add-question" aria-label="%s">%s</button>',
5157 esc_attr(__('Add question', 'mxchat')),
5158 esc_html__('Add Question', 'mxchat')
5159 );
5160 echo '<p class="description">' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '</p>';
5161 echo '</div>';
5162 }
5163
5164 public function mxchat_brave_api_key_callback() {
5165 $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : '';
5166
5167 echo '<div class="api-key-wrapper">';
5168 echo sprintf(
5169 '<input type="password" id="brave_api_key" name="brave_api_key" value="%s" class="regular-text" />',
5170 $brave_api_key
5171 );
5172 echo '<button type="button" id="toggleBraveApiKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5173 echo '</div>';
5174 echo '<p class="description">' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '</p>';
5175 }
5176
5177 public function mxchat_brave_image_count_callback() {
5178 $brave_image_count = isset($this->options['brave_image_count'])
5179 ? intval($this->options['brave_image_count'])
5180 : 4;
5181
5182 echo sprintf(
5183 '<input type="number" id="brave_image_count" name="brave_image_count"
5184 value="%d" min="1" max="6" class="small-text" />',
5185 $brave_image_count
5186 );
5187 echo '<p class="description">' . __('Select the number of images to return (1-6).', 'mxchat') . '</p>';
5188 }
5189
5190 public function mxchat_brave_safe_search_callback() {
5191 $brave_safe_search = isset($this->options['brave_safe_search'])
5192 ? esc_attr($this->options['brave_safe_search'])
5193 : 'strict';
5194
5195 echo '<select id="brave_safe_search" name="brave_safe_search">';
5196 echo sprintf(
5197 '<option value="strict" %s>%s</option>',
5198 selected($brave_safe_search, 'strict', false),
5199 __('Strict', 'mxchat')
5200 );
5201 echo sprintf(
5202 '<option value="off" %s>%s</option>',
5203 selected($brave_safe_search, 'off', false),
5204 __('Off', 'mxchat')
5205 );
5206 echo '</select>';
5207 echo '<p class="description">' .
5208 esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') .
5209 '</p>';
5210 }
5211
5212 public function mxchat_brave_news_count_callback() {
5213 $brave_news_count = isset($this->options['brave_news_count'])
5214 ? intval($this->options['brave_news_count'])
5215 : 3;
5216
5217 echo sprintf(
5218 '<input type="number" id="brave_news_count" name="brave_news_count"
5219 value="%d" min="1" max="10" class="small-text" />',
5220 $brave_news_count
5221 );
5222 echo '<p class="description">' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '</p>';
5223 }
5224
5225 public function mxchat_brave_country_callback() {
5226 $brave_country = isset($this->options['brave_country'])
5227 ? esc_attr($this->options['brave_country'])
5228 : 'us';
5229
5230 echo sprintf(
5231 '<input type="text" id="brave_country" name="brave_country"
5232 value="%s" maxlength="2" class="small-text" />',
5233 $brave_country
5234 );
5235 echo '<p class="description">' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '</p>';
5236 }
5237
5238 public function mxchat_brave_language_callback() {
5239 $brave_language = isset($this->options['brave_language'])
5240 ? esc_attr($this->options['brave_language'])
5241 : 'en';
5242
5243 echo sprintf(
5244 '<input type="text" id="brave_language" name="brave_language"
5245 value="%s" maxlength="2" class="small-text" />',
5246 $brave_language
5247 );
5248 echo '<p class="description">' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '</p>';
5249 }
5250
5251
5252
5253
5254
5255 // Section Callback
5256 public function mxchat_pdf_intent_section_callback() {
5257 echo '<p>' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '</p>';
5258 }
5259
5260 public function mxchat_chat_toolbar_toggle_callback() {
5261 // Get chat toolbar toggle value with fallback
5262 $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off';
5263 $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : '';
5264
5265 // Output the toggle switch
5266 echo '<label class="toggle-switch">';
5267 echo sprintf(
5268 '<input type="checkbox" id="chat_toolbar_toggle" name="chat_toolbar_toggle" value="on" %s />',
5269 esc_attr($checked)
5270 );
5271 echo '<span class="slider"></span>';
5272 echo '</label>';
5273
5274 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>';
5275 }
5276 /**
5277 * Callback for PDF upload button toggle setting
5278 */
5279 public function mxchat_show_pdf_upload_button_callback() {
5280 // Get toggle value with fallback
5281 $show_pdf_button = isset($this->options['show_pdf_upload_button']) ? $this->options['show_pdf_upload_button'] : 'on';
5282 $checked = ($show_pdf_button === 'on') ? 'checked' : '';
5283
5284 // Output the toggle switch
5285 echo '<label class="toggle-switch">';
5286 echo sprintf(
5287 '<input type="checkbox" id="show_pdf_upload_button" name="show_pdf_upload_button" value="on" %s />',
5288 esc_attr($checked)
5289 );
5290 echo '<span class="slider"></span>';
5291 echo '</label>';
5292
5293 echo '<p class="description">' . esc_html__('Enable to show the PDF upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5294 }
5295 /**
5296 * Callback for Word upload button toggle setting
5297 */
5298 public function mxchat_show_word_upload_button_callback() {
5299 // Get toggle value with fallback
5300 $show_word_button = isset($this->options['show_word_upload_button']) ? $this->options['show_word_upload_button'] : 'on';
5301 $checked = ($show_word_button === 'on') ? 'checked' : '';
5302
5303 // Output the toggle switch
5304 echo '<label class="toggle-switch">';
5305 echo sprintf(
5306 '<input type="checkbox" id="show_word_upload_button" name="show_word_upload_button" value="on" %s />',
5307 esc_attr($checked)
5308 );
5309 echo '<span class="slider"></span>';
5310 echo '</label>';
5311
5312 echo '<p class="description">' . esc_html__('Enable to show the Word document upload button in the chatbot toolbar. Disable to hide it.', 'mxchat') . '</p>';
5313 }
5314
5315 public function mxchat_pdf_intent_trigger_text_callback() {
5316 $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat');
5317
5318 echo sprintf(
5319 '<textarea id="pdf_intent_trigger_text"
5320 name="pdf_intent_trigger_text"
5321 rows="3"
5322 cols="50"
5323 placeholder="%s">%s</textarea>',
5324 esc_attr__('Enter trigger text', 'mxchat'),
5325 isset($this->options['pdf_intent_trigger_text'])
5326 ? esc_textarea($this->options['pdf_intent_trigger_text'])
5327 : esc_textarea($default_text)
5328 );
5329 echo '<p class="description">' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '</p>';
5330 }
5331
5332 public function mxchat_pdf_intent_success_text_callback() {
5333 $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat');
5334
5335 echo sprintf(
5336 '<textarea id="pdf_intent_success_text"
5337 name="pdf_intent_success_text"
5338 rows="3"
5339 cols="50"
5340 placeholder="%s">%s</textarea>',
5341 esc_attr__('Enter success text', 'mxchat'),
5342 isset($this->options['pdf_intent_success_text'])
5343 ? esc_textarea($this->options['pdf_intent_success_text'])
5344 : esc_textarea($default_text)
5345 );
5346 echo '<p class="description">' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '</p>';
5347 }
5348
5349 public function mxchat_pdf_intent_error_text_callback() {
5350 $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat');
5351
5352 echo sprintf(
5353 '<textarea id="pdf_intent_error_text"
5354 name="pdf_intent_error_text"
5355 rows="3"
5356 cols="50"
5357 placeholder="%s">%s</textarea>',
5358 esc_attr__('Enter error text', 'mxchat'),
5359 isset($this->options['pdf_intent_error_text'])
5360 ? esc_textarea($this->options['pdf_intent_error_text'])
5361 : esc_textarea($default_text)
5362 );
5363 echo '<p class="description">' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '</p>';
5364 }
5365
5366 public function mxchat_pdf_max_pages_callback() {
5367 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69;
5368
5369 echo sprintf(
5370 '<input type="range"
5371 id="pdf_max_pages"
5372 name="pdf_max_pages"
5373 min="1"
5374 max="69"
5375 value="%d"
5376 class="range-slider" />',
5377 esc_attr($max_pages)
5378 );
5379 echo '<span id="pdf_max_pages_output">' . esc_html($max_pages) . '</span>';
5380 echo '<p class="description">' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '</p>';
5381 }
5382
5383 public function mxchat_live_agent_status_callback() {
5384 // Always get fresh options instead of using cached $this->options
5385 $fresh_options = get_option('mxchat_options');
5386 $status = isset($fresh_options['live_agent_status']) ? $fresh_options['live_agent_status'] : 'off';
5387
5388 echo '<label class="toggle-switch">';
5389 echo sprintf(
5390 '<input type="checkbox" id="live_agent_status" name="live_agent_status" value="on" %s />',
5391 checked($status, 'on', false)
5392 );
5393 echo '<span class="slider"></span>';
5394 echo '</label>';
5395 echo '<label for="live_agent_status" class="mxchat-status-label">';
5396 echo '<span class="status-text">' . ($status === 'on' ? esc_html__('Online', 'mxchat') : esc_html__('Offline', 'mxchat')) . '</span>';
5397 echo '</label>';
5398 }
5399
5400 public function mxchat_live_agent_away_message_callback() {
5401 $message = isset($this->options['live_agent_away_message'])
5402 ? $this->options['live_agent_away_message']
5403 : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat');
5404
5405 printf(
5406 '<textarea id="live_agent_away_message" name="live_agent_away_message" rows="3" cols="50">%s</textarea>',
5407 esc_textarea($message)
5408 );
5409 echo '<p class="description">' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '</p>';
5410 }
5411
5412 public function mxchat_live_agent_notification_message_callback() {
5413 $message = isset($this->options['live_agent_notification_message'])
5414 ? $this->options['live_agent_notification_message']
5415 : __('Live agent has been notified.', 'mxchat');
5416
5417 printf(
5418 '<textarea id="live_agent_notification_message" name="live_agent_notification_message" rows="3" cols="50">%s</textarea>',
5419 esc_textarea($message)
5420 );
5421 echo '<p class="description">' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '</p>';
5422 }
5423
5424 public function mxchat_live_agent_webhook_url_callback() {
5425 $webhook_url = isset($this->options['live_agent_webhook_url'])
5426 ? esc_url($this->options['live_agent_webhook_url'])
5427 : esc_url(get_option('live_agent_webhook_url', ''));
5428
5429 printf(
5430 '<input type="password" id="live_agent_webhook_url" name="live_agent_webhook_url" value="%s" class="regular-text" />',
5431 $webhook_url
5432 );
5433 echo '<button type="button" id="toggleWebhookUrlVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5434 echo '<p class="description">' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '</p>';
5435 }
5436
5437 public function mxchat_live_agent_secret_key_callback() {
5438 printf(
5439 '<input type="password" id="live_agent_secret_key" name="live_agent_secret_key" value="%s" class="regular-text" />',
5440 isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : ''
5441 );
5442 echo '<button type="button" id="toggleSecretKeyVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5443 echo '<p class="description">' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '</p>';
5444 }
5445
5446 public function mxchat_live_agent_bot_token_callback() {
5447 printf(
5448 '<input type="password" id="live_agent_bot_token" name="live_agent_bot_token" value="%s" class="regular-text" />',
5449 isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : ''
5450 );
5451 echo '<button type="button" id="toggleBotTokenVisibility">' . esc_html__('Show', 'mxchat') . '</button>';
5452 echo '<p class="description">' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '</p>';
5453 }
5454
5455 public function mxchat_live_agent_user_ids_callback() {
5456 $user_ids = isset($this->options['live_agent_user_ids'])
5457 ? esc_textarea($this->options['live_agent_user_ids'])
5458 : '';
5459
5460 printf(
5461 '<textarea id="live_agent_user_ids" name="live_agent_user_ids" rows="4" class="large-text">%s</textarea>',
5462 $user_ids
5463 );
5464 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>';
5465 }
5466
5467 public function mxchat_similarity_threshold_callback() {
5468 // Load from mxchat_options array
5469 $options = get_option('mxchat_options', []);
5470
5471 // Get value from options array with default of 80
5472 $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : 35;
5473
5474 echo '<div class="slider-container">';
5475 echo sprintf(
5476 '<input type="range"
5477 id="similarity_threshold"
5478 name="similarity_threshold"
5479 min="20"
5480 max="85"
5481 step="1"
5482 value="%s"
5483 class="range-slider" />',
5484 esc_attr($threshold)
5485 );
5486 echo sprintf(
5487 '<span id="threshold_value" class="range-value">%s</span>',
5488 esc_html($threshold)
5489 );
5490 echo '</div>';
5491 echo '<p class="description">';
5492 echo sprintf(
5493 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'),
5494 '<a href="' . admin_url('admin.php?page=mxchat-addons') . '">',
5495 '</a>'
5496 );
5497 echo '</p>';
5498 }
5499
5500 public function mxchat_enqueue_admin_assets() {
5501 // Get plugin version (define this in your main plugin file)
5502 $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '2.2.6';
5503
5504 // Use file modification time for development (remove in production)
5505 if (defined('WP_DEBUG') && WP_DEBUG) {
5506 $version = filemtime(plugin_dir_path(__FILE__) . '../mxchat-basic.php');
5507 }
5508
5509 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
5510 $plugin_url = plugin_dir_url(__FILE__) . '../';
5511
5512 // Only load on MxChat pages
5513 if (strpos($current_page, 'mxchat') === false) {
5514 return;
5515 }
5516
5517 // Always load these on all MxChat pages
5518 $this->enqueue_core_admin_assets($plugin_url, $version);
5519 $this->enqueue_page_specific_assets($current_page, $plugin_url, $version);
5520 $this->localize_admin_scripts($current_page);
5521 }
5522 private function enqueue_core_admin_assets($plugin_url, $version) {
5523 // Core admin styles
5524 wp_enqueue_style('mxchat-admin-css', $plugin_url . 'css/admin-style.css', array(), $version);
5525 wp_enqueue_style('mxchat-knowledge-css', $plugin_url . 'css/knowledge-style.css', array(), $version);
5526
5527 // Core admin scripts
5528 wp_enqueue_script('mxchat-admin-js', $plugin_url . 'js/mxchat-admin.js', array('jquery'), $version, true);
5529 }
5530 private function enqueue_page_specific_assets($current_page, $plugin_url, $version) {
5531 switch ($current_page) {
5532 case 'mxchat-prompts':
5533 // Knowledge processing page assets
5534 wp_enqueue_style('mxchat-content-selector-css', $plugin_url . 'css/content-selector.css', array(), $version);
5535 wp_enqueue_script('mxchat-content-selector-js', $plugin_url . 'js/content-selector.js', array('jquery'), $version, true);
5536 // NEW: Add the knowledge processing script
5537 wp_enqueue_script('mxchat-knowledge-processing', $plugin_url . 'js/knowledge-processing.js', array('jquery'), $version, true);
5538 break;
5539
5540 case 'mxchat-transcripts':
5541 wp_enqueue_style('mxchat-chat-transcripts-css', $plugin_url . 'css/chat-transcripts.css', array(), $version);
5542 wp_enqueue_script('mxchat-transcripts-js', $plugin_url . 'js/mxchat_transcripts.js', array('jquery'), $version, true);
5543 break;
5544
5545 case 'mxchat-actions':
5546 wp_enqueue_style('mxchat-intent-css', $plugin_url . 'css/intent-style.css', array(), $version);
5547 break;
5548
5549 case 'mxchat-activation':
5550 wp_enqueue_script('mxchat-activation-js', $plugin_url . 'js/activation-script.js', array('jquery'), $version, true);
5551 break;
5552
5553 }
5554 }
5555 private function localize_admin_scripts($current_page) {
5556 // Base localization data for main admin script
5557 $base_data = array(
5558 'ajax_url' => admin_url('admin-ajax.php'),
5559 'nonce' => wp_create_nonce('mxchat_status_nonce'),
5560 'admin_url' => admin_url(),
5561 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'),
5562 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'),
5563 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'),
5564 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'),
5565 'actions_nonce' => wp_create_nonce('mxchat_actions_nonce'),
5566 'add_intent_nonce' => wp_create_nonce('mxchat_add_intent_nonce'),
5567 'edit_intent_nonce' => wp_create_nonce('mxchat_edit_intent'),
5568 'toggle_action_nonce' => wp_create_nonce('mxchat_actions_nonce'),
5569 'is_activated' => $this->is_activated ? '1' : '0',
5570 'status_refresh_interval' => 5000,
5571 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5572 'ajaxurl' => admin_url('admin-ajax.php') // For jQuery fallback
5573 );
5574
5575 // Localize main admin script with base data
5576 wp_localize_script('mxchat-admin-js', 'mxchatAdmin', $base_data);
5577
5578 // Page-specific localizations
5579 $this->localize_page_specific_scripts($current_page);
5580 }
5581 private function localize_page_specific_scripts($current_page) {
5582 switch ($current_page) {
5583 case 'mxchat-prompts':
5584 // Status updater localization
5585 wp_localize_script('mxchat-status-updater', 'mxchat_status_data', array(
5586 'ajax_url' => admin_url('admin-ajax.php'),
5587 'nonce' => wp_create_nonce('mxchat_status_nonce')
5588 ));
5589
5590 // Content selector localization
5591 wp_localize_script('mxchat-content-selector-js', 'mxchatSelector', array(
5592 'ajaxurl' => admin_url('admin-ajax.php'),
5593 'nonce' => wp_create_nonce('mxchat_content_selector_nonce'),
5594 'i18n' => array(
5595 'searchPlaceholder' => __('Search posts and pages...', 'mxchat'),
5596 'selectAll' => __('Select All', 'mxchat'),
5597 'process' => __('Process Selected', 'mxchat'),
5598 'cancel' => __('Cancel', 'mxchat'),
5599 'noResults' => __('No content found.', 'mxchat')
5600 )
5601 ));
5602
5603 // NEW: Knowledge processing script localization using mxchatAdmin
5604 wp_localize_script('mxchat-knowledge-processing', 'mxchatAdmin', array(
5605 'ajax_url' => admin_url('admin-ajax.php'),
5606 'status_nonce' => wp_create_nonce('mxchat_status_nonce'), // Note: changed 'nonce' to 'status_nonce'
5607 'stop_nonce' => wp_create_nonce('mxchat_stop_processing_action'),
5608 'admin_url' => admin_url(),
5609 'ajaxurl' => admin_url('admin-ajax.php'),
5610 'status_refresh_interval' => 2000
5611 ));
5612 break;
5613
5614 case 'mxchat-activation':
5615 wp_localize_script('mxchat-activation-js', 'mxchatAdmin', array(
5616 'ajax_url' => admin_url('admin-ajax.php'),
5617 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce')
5618 ));
5619 break;
5620
5621 case 'mxchat-settings':
5622 default:
5623 // Color picker and settings localization
5624 wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array(
5625 'ajax_url' => admin_url('admin-ajax.php'),
5626 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off',
5627 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff',
5628 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121',
5629 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121',
5630 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff',
5631 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121',
5632 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121',
5633 'close_button_color' => $this->options['close_button_color'] ?? '#fff',
5634 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121',
5635 'icon_color' => $this->options['icon_color'] ?? '#fff',
5636 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121',
5637 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'),
5638 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
5639 'loops_api_key' => $this->options['loops_api_key'] ?? '',
5640 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '',
5641 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
5642 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
5643 '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'),
5644 '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'),
5645 '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'),
5646 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69,
5647 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '',
5648 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '',
5649 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '',
5650 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff',
5651 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333',
5652 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off',
5653 'show_pdf_upload_button' => $this->options['show_pdf_upload_button'] ?? 'on',
5654 'show_word_upload_button' => $this->options['show_word_upload_button'] ?? 'on',
5655 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676',
5656 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff',
5657 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121',
5658 ));
5659 break;
5660 }
5661
5662 // Additional localization that was in the original code
5663 wp_localize_script('mxchat-admin-js', 'mxchatPromptsAdmin', array(
5664 'ajax_url' => admin_url('admin-ajax.php'),
5665 'prompts_setting_nonce' => wp_create_nonce('mxchat_prompts_setting_nonce'),
5666 ));
5667 }
5668
5669 public function mxchat_sanitize($input) {
5670 $new_input = array();
5671
5672 if (isset($input['api_key'])) {
5673 $new_input['api_key'] = sanitize_text_field($input['api_key']);
5674 }
5675
5676 if (isset($input['similarity_threshold'])) {
5677 $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer
5678 $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 20), 85); // Enforce range
5679 }
5680
5681 if (isset($input['xai_api_key'])) {
5682 $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']);
5683 }
5684
5685 if (isset($input['claude_api_key'])) {
5686 $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']);
5687 }
5688
5689 if (isset($input['enable_streaming_toggle'])) {
5690 $new_input['enable_streaming_toggle'] = ($input['enable_streaming_toggle'] === 'on') ? 'on' : 'off';
5691 } else {
5692 // If checkbox not checked, it won't be in $input, so set to 'off'
5693 $new_input['enable_streaming_toggle'] = 'off';
5694 }
5695
5696
5697 if (isset($input['deepseek_api_key'])) {
5698 $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']);
5699 }
5700
5701 if (isset($input['gemini_api_key'])) {
5702 $new_input['gemini_api_key'] = sanitize_text_field($input['gemini_api_key']);
5703 }
5704
5705 if (isset($input['enable_woocommerce_integration'])) {
5706 $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off';
5707 }
5708
5709 if (isset($input['privacy_toggle'])) {
5710 $new_input['privacy_toggle'] = $input['privacy_toggle'];
5711 }
5712
5713 if (isset($input['complianz_toggle'])) {
5714 $new_input['complianz_toggle'] = $input['complianz_toggle'];
5715 }
5716
5717 // Handle custom privacy text input
5718 if (isset($input['privacy_text'])) {
5719 // Allow basic HTML for links
5720 $new_input['privacy_text'] = wp_kses_post($input['privacy_text']);
5721 }
5722
5723 if (isset($input['system_prompt_instructions'])) {
5724 $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']);
5725 }
5726
5727 if (isset($input['mxchat_pro_email'])) {
5728 $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']);
5729 }
5730
5731 if (isset($input['mxchat_activation_key'])) {
5732 $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']);
5733 }
5734
5735 if (isset($input['append_to_body'])) {
5736 $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off';
5737 }
5738
5739 if (isset($input['top_bar_title'])) {
5740 $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']);
5741 }
5742
5743 if (isset($input['ai_agent_text'])) {
5744 $new_input['ai_agent_text'] = sanitize_text_field($input['ai_agent_text']);
5745 }
5746
5747 if (isset($input['enable_email_block'])) {
5748 $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']);
5749 }
5750
5751 if (isset($input['email_blocker_header_content'])) {
5752 // wp_kses_post() allows standard HTML tags permitted by WordPress
5753 $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']);
5754 }
5755 if (isset($input['email_blocker_button_text'])) {
5756 $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']);
5757 }
5758
5759 if (isset($input['intro_message'])) {
5760 $new_input['intro_message'] = wp_kses_post($input['intro_message']); // Use wp_kses_post instead
5761 }
5762
5763 if (isset($input['input_copy'])) {
5764 $new_input['input_copy'] = sanitize_text_field($input['input_copy']);
5765 }
5766
5767 if (isset($input['rate_limit_message'])) {
5768 $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']);
5769 }
5770
5771 // Handle the new rate limits format
5772 if (isset($input['rate_limits']) && is_array($input['rate_limits'])) {
5773 $new_input['rate_limits'] = array();
5774 $allowed_limits = array('1', '3', '5', '10', '15', '20', '50', '100', 'unlimited');
5775 $allowed_timeframes = array('hourly', 'daily', 'weekly', 'monthly');
5776
5777 foreach ($input['rate_limits'] as $role_id => $settings) {
5778 $new_input['rate_limits'][$role_id] = array();
5779
5780 // Sanitize limit
5781 if (isset($settings['limit'])) {
5782 $limit = sanitize_text_field($settings['limit']);
5783 if (in_array($limit, $allowed_limits, true)) {
5784 $new_input['rate_limits'][$role_id]['limit'] = $limit;
5785 } else {
5786 $new_input['rate_limits'][$role_id]['limit'] = ($role_id === 'logged_out') ? '10' : '100'; // Default
5787 }
5788 }
5789
5790 // Sanitize timeframe
5791 if (isset($settings['timeframe'])) {
5792 $timeframe = sanitize_text_field($settings['timeframe']);
5793 if (in_array($timeframe, $allowed_timeframes, true)) {
5794 $new_input['rate_limits'][$role_id]['timeframe'] = $timeframe;
5795 } else {
5796 $new_input['rate_limits'][$role_id]['timeframe'] = 'daily'; // Default
5797 }
5798 }
5799
5800 // Sanitize message
5801 if (isset($settings['message'])) {
5802 $new_input['rate_limits'][$role_id]['message'] = sanitize_textarea_field($settings['message']);
5803 }
5804 }
5805 }
5806
5807 if (isset($input['pre_chat_message'])) {
5808 $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']);
5809 }
5810
5811 if (isset($input['voyage_api_key'])) {
5812 $new_input['voyage_api_key'] = sanitize_text_field($input['voyage_api_key']);
5813 }
5814
5815 // Add to your sanitize function
5816 if (isset($input['embedding_model'])) {
5817 $allowed_models = array(
5818 'text-embedding-ada-002',
5819 'text-embedding-3-small',
5820 'text-embedding-3-large',
5821 'voyage-3-large',
5822 'gemini-embedding-exp-03-07'
5823 );
5824 if (in_array($input['embedding_model'], $allowed_models)) {
5825 $new_input['embedding_model'] = sanitize_text_field($input['embedding_model']);
5826 }
5827 }
5828
5829 if (isset($input['model'])) {
5830 $allowed_models = array(
5831 'gemini-2.0-flash',
5832 'gemini-2.0-flash-lite',
5833 'gemini-1.5-pro',
5834 'gemini-1.5-flash',
5835 'grok-3-beta',
5836 'grok-3-fast-beta',
5837 'grok-3-mini-beta',
5838 'grok-3-mini-fast-beta',
5839 'grok-2',
5840 'deepseek-chat',
5841 'claude-opus-4-20250514',
5842 'claude-sonnet-4-20250514',
5843 'claude-3-7-sonnet-20250219',
5844 'claude-3-5-sonnet-20241022',
5845 'claude-3-opus-20240229',
5846 'claude-3-sonnet-20240229',
5847 'claude-3-haiku-20240307',
5848 'gpt-4o',
5849 'gpt-4.1-2025-04-14',
5850 'gpt-4o-mini',
5851 'gpt-4-turbo',
5852 'gpt-4',
5853 'gpt-3.5-turbo',
5854 );
5855 if (in_array($input['model'], $allowed_models)) {
5856 $new_input['model'] = sanitize_text_field($input['model']);
5857 }
5858 }
5859
5860 if (isset($input['close_button_color'])) {
5861 $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']);
5862 }
5863
5864 if (isset($input['chatbot_bg_color'])) {
5865 $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']);
5866 }
5867
5868 if (isset($input['woocommerce_consumer_key'])) {
5869 $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']);
5870 }
5871
5872 if (isset($input['woocommerce_consumer_secret'])) {
5873 $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']);
5874 }
5875
5876 if (isset($input['user_message_bg_color'])) {
5877 $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']);
5878 }
5879
5880 if (isset($input['user_message_font_color'])) {
5881 $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']);
5882 }
5883
5884 if (isset($input['bot_message_bg_color'])) {
5885 $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']);
5886 }
5887
5888 if (isset($input['bot_message_font_color'])) {
5889 $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']);
5890 }
5891
5892 if (isset($input['live_agent_message_bg_color'])) {
5893 $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']);
5894 }
5895
5896 if (isset($input['live_agent_message_font_color'])) {
5897 $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']);
5898 }
5899
5900 if (isset($input['mode_indicator_bg_color'])) {
5901 $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']);
5902 }
5903
5904 if (isset($input['mode_indicator_font_color'])) {
5905 $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']);
5906 }
5907
5908 if (isset($input['toolbar_icon_color'])) {
5909 $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']);
5910 }
5911
5912 if (isset($input['top_bar_bg_color'])) {
5913 $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']);
5914 }
5915
5916 if (isset($input['send_button_font_color'])) {
5917 $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']);
5918 }
5919
5920 if (isset($input['chatbot_background_color'])) {
5921 $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']);
5922 }
5923
5924 if (isset($input['icon_color'])) {
5925 $new_input['icon_color'] = sanitize_hex_color($input['icon_color']);
5926 }
5927
5928 if (isset($input['custom_icon'])) {
5929 $new_input['custom_icon'] = esc_url_raw($input['custom_icon']);
5930 }
5931
5932 if (isset($input['title_icon'])) {
5933 $new_input['title_icon'] = esc_url_raw($input['title_icon']);
5934 }
5935
5936 if (isset($input['chat_input_font_color'])) {
5937 $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']);
5938 }
5939
5940 // Sanitize link_target_toggle
5941 if (isset($input['link_target_toggle'])) {
5942 $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off';
5943 }
5944
5945 // Sanitize Loops API Key
5946 if (isset($input['loops_api_key'])) {
5947 $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']);
5948 }
5949
5950 if (isset($input['chat_persistence_toggle'])) {
5951 $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off';
5952 }
5953
5954 if (isset($input['popular_question_1'])) {
5955 $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']);
5956 }
5957
5958 if (isset($input['popular_question_2'])) {
5959 $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']);
5960 }
5961
5962 if (isset($input['popular_question_3'])) {
5963 $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']);
5964 }
5965
5966 if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) {
5967 $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']);
5968 }
5969
5970 // Sanitize Loops Mailing List
5971 if (isset($input['loops_mailing_list'])) {
5972 $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']);
5973 }
5974
5975 // Sanitize Triggered Phrase Response
5976 if (isset($input['triggered_phrase_response'])) {
5977 $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']);
5978 }
5979
5980 if (isset($input['email_capture_response'])) {
5981 $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']);
5982 }
5983
5984 // Sanitize Brave Search Settings
5985 if (isset($input['brave_api_key'])) {
5986 $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']);
5987 }
5988
5989 if (isset($input['brave_image_count'])) {
5990 $image_count = intval($input['brave_image_count']);
5991 $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4;
5992 }
5993
5994 if (isset($input['brave_safe_search'])) {
5995 $allowed = array('strict', 'off');
5996 $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict';
5997 }
5998
5999 if (isset($input['brave_news_count'])) {
6000 $news_count = intval($input['brave_news_count']);
6001 $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3;
6002 }
6003
6004 if (isset($input['brave_country'])) {
6005 $new_input['brave_country'] = sanitize_text_field($input['brave_country']);
6006 }
6007
6008 if (isset($input['brave_language'])) {
6009 $new_input['brave_language'] = sanitize_text_field($input['brave_language']);
6010 }
6011
6012 if (isset($input['chat_toolbar_toggle'])) {
6013 $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off';
6014 }
6015
6016 // Sanitize PDF upload button toggle
6017 if (isset($input['show_pdf_upload_button'])) {
6018 $new_input['show_pdf_upload_button'] = $input['show_pdf_upload_button'] === 'on' ? 'on' : 'off';
6019 } else {
6020 $new_input['show_pdf_upload_button'] = 'off'; // If checkbox is unchecked
6021 }
6022
6023 // Sanitize Word upload button toggle
6024 if (isset($input['show_word_upload_button'])) {
6025 $new_input['show_word_upload_button'] = $input['show_word_upload_button'] === 'on' ? 'on' : 'off';
6026 } else {
6027 $new_input['show_word_upload_button'] = 'off'; // If checkbox is unchecked
6028 }
6029
6030 if (isset($input['pdf_intent_trigger_text'])) {
6031 $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']);
6032 }
6033
6034 if (isset($input['pdf_intent_success_text'])) {
6035 $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']);
6036 }
6037
6038 if (isset($input['pdf_intent_error_text'])) {
6039 $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']);
6040 }
6041
6042 if (isset($input['pdf_max_pages'])) {
6043 $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']);
6044 if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) {
6045 $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range
6046 }
6047 }
6048
6049 if (isset($input['live_agent_webhook_url'])) {
6050 $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']);
6051 }
6052 if (isset($input['live_agent_secret_key'])) {
6053 $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']);
6054 }
6055
6056 // Live Agent Integration
6057 if (isset($input['live_agent_bot_token'])) {
6058 $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']);
6059 }
6060
6061 if (isset($input['live_agent_user_ids'])) {
6062 $new_input['live_agent_user_ids'] = sanitize_textarea_field($input['live_agent_user_ids']);
6063 }
6064
6065 if (isset($input['live_agent_status'])) {
6066 $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off';
6067 }
6068 if (isset($input['live_agent_away_message'])) {
6069 $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']);
6070 }
6071 if (isset($input['live_agent_notification_message'])) {
6072 $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']);
6073 }
6074
6075 return $new_input;
6076 }
6077
6078
6079 // Method to append the chatbot to the body
6080 public function mxchat_append_chatbot_to_body() {
6081 $options = get_option('mxchat_options');
6082 if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') {
6083 echo do_shortcode('[mxchat_chatbot floating="yes"]');
6084 }
6085 }
6086
6087
6088
6089
6090
6091 private function mxchat_fetch_loops_mailing_lists($api_key) {
6092 $url = 'https://app.loops.so/api/v1/lists';
6093 $response = wp_remote_get($url, array(
6094 'headers' => array(
6095 'Authorization' => 'Bearer ' . $api_key,
6096 'Content-Type' => 'application/json'
6097 )
6098 ));
6099
6100 if (is_wp_error($response)) {
6101 return array();
6102 }
6103
6104 $body = wp_remote_retrieve_body($response);
6105 $lists = json_decode($body, true);
6106
6107 return isset($lists) && is_array($lists) ? $lists : array();
6108 }
6109
6110 function mxchat_calculate_cosine_similarity($vec1, $vec2) {
6111 if (empty($vec1) || empty($vec2)) {
6112 return 0.0;
6113 }
6114
6115 $dot_product = 0.0;
6116 $norm_a = 0.0;
6117 $norm_b = 0.0;
6118
6119 for ($i = 0; $i < count($vec1); $i++) {
6120 $dot_product += $vec1[$i] * $vec2[$i];
6121 $norm_a += pow($vec1[$i], 2);
6122 $norm_b += pow($vec2[$i], 2);
6123 }
6124
6125 if ($norm_a == 0.0 || $norm_b == 0.0) {
6126 return 0.0;
6127 } else {
6128 return $dot_product / (sqrt($norm_a) * sqrt($norm_b));
6129 }
6130 }
6131
6132 /**
6133 * Validates nonce and deletes all chat prompts
6134 */
6135 public function mxchat_handle_delete_all_prompts() {
6136 // Verify nonce
6137 if (!isset($_POST['mxchat_delete_all_prompts_nonce']) || !wp_verify_nonce($_POST['mxchat_delete_all_prompts_nonce'], 'mxchat_delete_all_prompts_action')) {
6138 wp_die(__('Nonce verification failed.', 'mxchat'));
6139 }
6140 // Check permissions
6141 if (!current_user_can('manage_options')) {
6142 wp_die(__('You do not have sufficient permissions to delete all prompts.', 'mxchat'));
6143 }
6144 $success = true;
6145 $error_messages = array();
6146 // Check if Pinecone is enabled and configured
6147 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6148 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6149 if ($use_pinecone && !empty($pinecone_options['mxchat_pinecone_api_key'])) {
6150 //error_log('[MXCHAT-DELETE] Pinecone is enabled - deleting all from Pinecone');
6151 // Get Pinecone configuration
6152 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6153 $pinecone_manager = new MxChat_Pinecone_Manager();
6154 $result = $pinecone_manager->mxchat_delete_all_from_pinecone($pinecone_options);
6155 if (!$result['success']) {
6156 $success = false;
6157 $error_messages[] = $result['message'];
6158 }
6159 // Clear Pinecone vector cache
6160 delete_option('mxchat_pinecone_vector_ids_cache');
6161 // Clear processed content caches
6162 delete_option('mxchat_pinecone_processed_cache');
6163 delete_option('mxchat_processed_content_cache');
6164
6165 // CLEAR THE PINECONE RECORDS CACHE (NEW LINE)
6166 delete_transient('mxchat_pinecone_recent_1k_cache');
6167
6168 } else {
6169 //error_log('[MXCHAT-DELETE] Pinecone not enabled - deleting from WordPress database');
6170 // Delete from WordPress database
6171 global $wpdb;
6172 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6173 $result = $wpdb->query("DELETE FROM {$table_name}");
6174 if ($result === false) {
6175 $success = false;
6176 $error_messages[] = 'Failed to delete from WordPress database';
6177 }
6178 }
6179 // Clear relevant cache
6180 wp_cache_delete('all_prompts', 'mxchat_prompts');
6181 // Set appropriate admin notice
6182 if ($success) {
6183 set_transient('mxchat_admin_notice_success',
6184 __('All prompts deleted successfully.', 'mxchat'), 30);
6185 } else {
6186 set_transient('mxchat_admin_notice_error',
6187 __('Failed to delete all prompts: ', 'mxchat') . implode(', ', $error_messages), 30);
6188 }
6189 // Redirect back with a success message
6190 $redirect_url = add_query_arg(array(
6191 'page' => 'mxchat-prompts',
6192 'all_deleted' => $success ? 'true' : 'false'
6193 ), admin_url('admin.php'));
6194 wp_safe_redirect($redirect_url);
6195 exit;
6196 }
6197
6198 /**
6199 * Handles deletion prompt with nonce validation
6200 */
6201 public function mxchat_handle_delete_prompt() {
6202 // Sanitize and validate nonce
6203 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
6204 if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) {
6205 wp_die(esc_html__('Nonce verification failed.', 'mxchat'));
6206 }
6207
6208 // Check permissions
6209 if (!current_user_can('manage_options')) {
6210 wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat'));
6211 }
6212
6213 // Get ID and source parameters
6214 $id = isset($_GET['id']) ? sanitize_text_field($_GET['id']) : '';
6215 $source = isset($_GET['source']) ? sanitize_text_field($_GET['source']) : '';
6216
6217 if (empty($id)) {
6218 wp_die(esc_html__('Invalid prompt ID.', 'mxchat'));
6219 }
6220
6221 $success = false;
6222 $error_message = '';
6223
6224 // Check if Pinecone is enabled and determine source automatically if not specified
6225 $pinecone_options = get_option('mxchat_pinecone_addon_options', array());
6226 $use_pinecone = ($pinecone_options['mxchat_use_pinecone'] ?? '0') === '1';
6227
6228 // If source is not specified, determine based on Pinecone configuration
6229 if (empty($source)) {
6230 $source = $use_pinecone ? 'pinecone' : 'wordpress';
6231 }
6232
6233 if ($source === 'pinecone' || $use_pinecone) {
6234 //error_log('[MXCHAT-DELETE] Deleting from Pinecone, ID: ' . $id);
6235
6236 // Handle Pinecone deletion
6237 if (empty($pinecone_options['mxchat_pinecone_host']) ||
6238 empty($pinecone_options['mxchat_pinecone_api_key'])) {
6239 wp_die(esc_html__('Pinecone configuration is missing.', 'mxchat'));
6240 }
6241
6242 // Delete from Pinecone using the vector ID directly
6243 $pinecone_manager = MxChat_Pinecone_Manager::get_instance();
6244 $result = $pinecone_manager->mxchat_delete_from_pinecone_by_vector_id(
6245 $id,
6246 $pinecone_options['mxchat_pinecone_api_key'],
6247 $pinecone_options['mxchat_pinecone_host']
6248 );
6249 if ($result['success']) {
6250 $success = true;
6251
6252 // Remove from vector cache
6253 $pinecone_manager->mxchat_remove_from_pinecone_vector_cache($id);
6254 $pinecone_manager->mxchat_remove_from_processed_content_caches($id);
6255
6256 set_transient('mxchat_admin_notice_success',
6257 esc_html__('Vector deleted successfully from Pinecone.', 'mxchat'), 30);
6258 } else {
6259 $error_message = $result['message'];
6260 set_transient('mxchat_admin_notice_error',
6261 esc_html__('Failed to delete from Pinecone: ', 'mxchat') . esc_html($error_message), 30);
6262 }
6263 } else {
6264 //error_log('[MXCHAT-DELETE] Deleting from WordPress database, ID: ' . $id);
6265
6266 // Handle WordPress database deletion
6267 global $wpdb;
6268 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
6269
6270 // Clear cache and delete prompt
6271 wp_cache_delete('prompt_' . $id, 'mxchat_prompts');
6272
6273 $result = $wpdb->delete(
6274 $table_name,
6275 array('id' => intval($id)),
6276 array('%d')
6277 );
6278
6279 if ($result !== false) {
6280 $success = true;
6281 set_transient('mxchat_admin_notice_success',
6282 esc_html__('Entry deleted successfully.', 'mxchat'), 30);
6283 } else {
6284 set_transient('mxchat_admin_notice_error',
6285 esc_html__('Failed to delete entry from database.', 'mxchat'), 30);
6286 }
6287 }
6288
6289 // Redirect back to the prompts page
6290 wp_safe_redirect(add_query_arg(
6291 array(
6292 'page' => 'mxchat-prompts',
6293 'deleted' => $success ? 'true' : 'false'
6294 ),
6295 admin_url('admin.php')
6296 ));
6297 exit;
6298 }
6299
6300 /**
6301 * Validates user permissions for editing chat settings
6302 */
6303 public function mxchat_handle_edit_intent() {
6304 // Security checks (nonce and permissions)
6305 if (!current_user_can('manage_options')) {
6306 wp_die(esc_html__('Unauthorized user', 'mxchat'));
6307 }
6308 check_admin_referer('mxchat_edit_intent');
6309
6310 // Get POST data
6311 $intent_id = isset($_POST['intent_id']) ? absint($_POST['intent_id']) : 0;
6312 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
6313 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
6314 $threshold_percentage = isset($_POST['similarity_threshold']) ? intval($_POST['similarity_threshold']) : 85;
6315 $similarity_threshold = min(95, max(70, $threshold_percentage)) / 100; // Convert to 0.70–0.95
6316
6317 // Validate inputs
6318 if (!$intent_id || empty($intent_label) || empty($phrases_input)) {
6319 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
6320 return;
6321 }
6322
6323 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
6324 if (empty($phrases_array)) {
6325 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
6326 return;
6327 }
6328
6329 // Generate embeddings with improved error handling
6330 $vectors = [];
6331 $failed_phrases = [];
6332
6333 foreach ($phrases_array as $phrase) {
6334 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
6335 if (is_array($embedding_vector)) {
6336 $vectors[] = $embedding_vector;
6337 } else {
6338 $failed_phrases[] = $phrase;
6339 }
6340 }
6341
6342 if (!empty($failed_phrases)) {
6343 $this->handle_embedding_error(
6344 sprintf(
6345 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
6346 implode(', ', $failed_phrases)
6347 )
6348 );
6349 return;
6350 }
6351
6352 if (empty($vectors)) {
6353 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
6354 return;
6355 }
6356
6357 $combined_vector = $this->mxchat_average_vectors($vectors);
6358 $serialized_vector = maybe_serialize($combined_vector);
6359
6360 // Update the database
6361 global $wpdb;
6362 $table_name = $wpdb->prefix . 'mxchat_intents';
6363
6364 $result = $wpdb->update(
6365 $table_name,
6366 array(
6367 'intent_label' => $intent_label,
6368 'phrases' => implode(', ', $phrases_array),
6369 'embedding_vector' => $serialized_vector,
6370 'similarity_threshold' => $similarity_threshold
6371 ),
6372 array('id' => $intent_id),
6373 array('%s', '%s', '%s', '%f'), // Format: string, string, string, float
6374 array('%d') // Where format: integer
6375 );
6376
6377 if (false === $result) {
6378 $this->handle_embedding_error(__('Failed to update action in database.', 'mxchat'));
6379 return;
6380 }
6381
6382 // Set success message and redirect
6383 set_transient('mxchat_admin_notice_success', __('Intent updated successfully!', 'mxchat'), 60);
6384
6385 $redirect_url = add_query_arg(
6386 array(
6387 'page' => 'mxchat-actions'
6388 ),
6389 admin_url('admin.php')
6390 );
6391 wp_safe_redirect($redirect_url);
6392 exit;
6393 }
6394 /**
6395 * Checks user permissions for managing options
6396 */
6397 public function mxchat_handle_add_intent() {
6398 if (!current_user_can('manage_options')) {
6399 wp_die(esc_html__('Unauthorized user', 'mxchat'));
6400 }
6401 check_admin_referer('mxchat_add_intent_nonce');
6402
6403 global $wpdb;
6404 $table_name = $wpdb->prefix . 'mxchat_intents';
6405
6406 // Sanitize and get form data
6407 $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : '';
6408 $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : '';
6409 $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : '';
6410
6411 // Get similarity threshold from form (convert percentage to decimal)
6412 $similarity_threshold = isset($_POST['similarity_threshold']) ? floatval($_POST['similarity_threshold']) / 100 : 0.85;
6413
6414 // Validate required fields
6415 if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) {
6416 $this->handle_embedding_error(__('Invalid input. Please ensure all fields are filled out.', 'mxchat'));
6417 return;
6418 }
6419
6420 // Validate callback function
6421 $available_callbacks = $this->mxchat_get_available_callbacks();
6422 if (!array_key_exists($callback_function, $available_callbacks)) {
6423 $this->handle_embedding_error(__('Invalid callback function selected.', 'mxchat'));
6424 return;
6425 }
6426
6427 // Check Pro requirements
6428 $is_pro_only = $available_callbacks[$callback_function]['pro_only'];
6429 if ($is_pro_only && !$this->is_activated) {
6430 $this->handle_embedding_error(__('This callback function is available in the Pro version only.', 'mxchat'));
6431 return;
6432 }
6433
6434 // Process phrases
6435 $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input))));
6436 if (empty($phrases_array)) {
6437 $this->handle_embedding_error(__('Please enter at least one valid phrase.', 'mxchat'));
6438 return;
6439 }
6440
6441 // Generate embeddings with improved error handling
6442 $vectors = [];
6443 $failed_phrases = [];
6444 foreach ($phrases_array as $phrase) {
6445 $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']);
6446 if (is_array($embedding_vector)) {
6447 $vectors[] = $embedding_vector;
6448 } else {
6449 $failed_phrases[] = $phrase;
6450 }
6451 }
6452
6453 // Check for embedding failures
6454 if (!empty($failed_phrases)) {
6455 $this->handle_embedding_error(
6456 sprintf(
6457 __('Error generating embeddings for phrases: %s. Check your embedding API.', 'mxchat'),
6458 implode(', ', $failed_phrases)
6459 )
6460 );
6461 return;
6462 }
6463
6464 if (empty($vectors)) {
6465 $this->handle_embedding_error(__('No valid embeddings generated. Please check your phrases.', 'mxchat'));
6466 return;
6467 }
6468
6469 // Create combined vector and insert into database
6470 $combined_vector = $this->mxchat_average_vectors($vectors);
6471 $serialized_vector = maybe_serialize($combined_vector);
6472
6473 $result = $wpdb->insert($table_name, [
6474 'intent_label' => $intent_label,
6475 'phrases' => implode(', ', $phrases_array),
6476 'embedding_vector' => $serialized_vector,
6477 'callback_function' => $callback_function,
6478 'similarity_threshold' => $similarity_threshold, // Now uses the actual form value
6479 ]);
6480
6481 if ($result === false) {
6482 $this->handle_embedding_error(__('Database error: ', 'mxchat') . $wpdb->last_error);
6483 return;
6484 }
6485
6486 // Set success message and redirect
6487 set_transient('mxchat_admin_notice_success', __('New intent added successfully!', 'mxchat'), 60);
6488 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions'));
6489 exit;
6490 }
6491
6492 /**
6493 * Averages multiple vectors into a single vector
6494 */
6495 private function mxchat_average_vectors($vectors) {
6496 $vector_length = count($vectors[0]);
6497 $sum_vector = array_fill(0, $vector_length, 0);
6498
6499 foreach ($vectors as $vector) {
6500 for ($i = 0; $i < $vector_length; $i++) {
6501 $sum_vector[$i] += $vector[$i];
6502 }
6503 }
6504
6505 // Divide each component by the number of vectors to get the average
6506 $num_vectors = count($vectors);
6507 for ($i = 0; $i < $vector_length; $i++) {
6508 $sum_vector[$i] /= $num_vectors;
6509 }
6510
6511 return $sum_vector;
6512 }
6513
6514
6515
6516
6517 /**
6518 * Generates embeddings from input text for MXChat
6519 */
6520 public function mxchat_generate_embedding($text) {
6521 // Enable detailed logging for debugging
6522 //error_log('[MXCHAT-EMBED] Starting embedding generation. Text length: ' . strlen($text) . ' bytes');
6523 //error_log('[MXCHAT-EMBED] Text preview: ' . substr($text, 0, 100) . '...');
6524
6525 $options = get_option('mxchat_options');
6526 $selected_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
6527 //error_log('[MXCHAT-EMBED] Selected embedding model: ' . $selected_model);
6528
6529 // Determine provider and endpoint
6530 if (strpos($selected_model, 'voyage') === 0) {
6531 $api_key = $options['voyage_api_key'] ?? '';
6532 $endpoint = 'https://api.voyageai.com/v1/embeddings';
6533 $provider_name = 'Voyage AI';
6534 //error_log('[MXCHAT-EMBED] Using Voyage AI API');
6535 } elseif (strpos($selected_model, 'gemini-embedding') === 0) {
6536 $api_key = $options['gemini_api_key'] ?? '';
6537 $endpoint = 'https://generativelanguage.googleapis.com/v1beta/models/' . $selected_model . ':embedContent';
6538 $provider_name = 'Google Gemini';
6539 //error_log('[MXCHAT-EMBED] Using Google Gemini API');
6540 } else {
6541 $api_key = $options['api_key'] ?? '';
6542 $endpoint = 'https://api.openai.com/v1/embeddings';
6543 $provider_name = 'OpenAI';
6544 //error_log('[MXCHAT-EMBED] Using OpenAI API');
6545 }
6546
6547 //error_log('[MXCHAT-EMBED] Using endpoint: ' . $endpoint);
6548
6549 if (empty($api_key)) {
6550 $error_message = sprintf('Missing %s API key. Please configure your API key in the MxChat settings.', $provider_name);
6551 //error_log('[MXCHAT-EMBED] Error: ' . $error_message);
6552 return $error_message;
6553 }
6554
6555 // Check if text is too long (for OpenAI, roughly estimate tokens as words/0.75)
6556 $estimated_tokens = ceil(str_word_count($text) / 0.75);
6557 //error_log('[MXCHAT-EMBED] Estimated token count: ~' . $estimated_tokens);
6558
6559 if ($estimated_tokens > 8000 && strpos($selected_model, 'voyage') === false && strpos($selected_model, 'gemini-embedding') === false) {
6560 //error_log('[MXCHAT-EMBED] Warning: Text may exceed OpenAI token limits (8K for most models)');
6561 // Consider truncating text here
6562 }
6563
6564 // Prepare request body based on provider
6565 if (strpos($selected_model, 'gemini-embedding') === 0) {
6566 // Gemini API format
6567 $request_body = array(
6568 'model' => 'models/' . $selected_model,
6569 'content' => array(
6570 'parts' => array(
6571 array('text' => $text)
6572 )
6573 )
6574 );
6575
6576 // Set output dimensionality to 1536 for consistency with other models
6577 $request_body['outputDimensionality'] = 1536;
6578 } else {
6579 // OpenAI/Voyage API format
6580 $request_body = array(
6581 'model' => $selected_model,
6582 'input' => $text
6583 );
6584
6585 // Add output_dimension for voyage-3-large model
6586 if ($selected_model === 'voyage-3-large') {
6587 $request_body['output_dimension'] = 2048;
6588 }
6589 }
6590
6591 //error_log('[MXCHAT-EMBED] Request prepared with model: ' . $selected_model);
6592
6593 // Prepare headers based on provider
6594 if (strpos($selected_model, 'gemini-embedding') === 0) {
6595 // Gemini uses API key as query parameter
6596 $endpoint .= '?key=' . $api_key;
6597 $headers = array(
6598 'Content-Type' => 'application/json'
6599 );
6600 } else {
6601 // OpenAI/Voyage use Bearer token
6602 $headers = array(
6603 'Authorization' => 'Bearer ' . $api_key,
6604 'Content-Type' => 'application/json'
6605 );
6606 }
6607
6608 // Make API request
6609 //error_log('[MXCHAT-EMBED] Sending API request to: ' . $endpoint);
6610 $response = wp_remote_post($endpoint, array(
6611 'body' => wp_json_encode($request_body),
6612 'headers' => $headers,
6613 'timeout' => 60 // Increased timeout for large inputs
6614 ));
6615
6616 // Handle wp_remote_post errors
6617 if (is_wp_error($response)) {
6618 $error_message = $response->get_error_message();
6619 //error_log('[MXCHAT-EMBED] WP Remote Post Error: ' . $error_message);
6620 return 'Connection error: ' . $error_message;
6621 }
6622
6623 // Get and check HTTP response code
6624 $http_code = wp_remote_retrieve_response_code($response);
6625 //error_log('[MXCHAT-EMBED] API Response Code: ' . $http_code);
6626
6627 if ($http_code !== 200) {
6628 $error_body = wp_remote_retrieve_body($response);
6629 //error_log('[MXCHAT-EMBED] API Error Response Body: ' . $error_body);
6630
6631 // Try to parse error for more details
6632 $error_json = json_decode($error_body, true);
6633 if (json_last_error() === JSON_ERROR_NONE && isset($error_json['error'])) {
6634 $error_type = $error_json['error']['type'] ?? 'unknown';
6635 $error_message = $error_json['error']['message'] ?? 'No message';
6636 //error_log('[MXCHAT-EMBED] API Error Type: ' . $error_type);
6637 //error_log('[MXCHAT-EMBED] API Error Message: ' . $error_message);
6638
6639 // Customize error message for common API errors
6640 if ($error_type === 'invalid_request_error' && strpos($error_message, 'API key') !== false) {
6641 $error_message = sprintf('Invalid %s API key. Please check your API key in the MxChat settings.', $provider_name);
6642 } elseif ($error_type === 'authentication_error') {
6643 $error_message = sprintf('%s authentication failed. Please verify your API key in the MxChat settings.', $provider_name);
6644 }
6645
6646 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
6647 return $error_message;
6648 }
6649
6650 $error_message = sprintf("API Error (HTTP %d): Unable to generate embedding", $http_code);
6651 //error_log('[MXCHAT-EMBED] Returning error: ' . $error_message);
6652 return $error_message;
6653 }
6654
6655 // Parse response body
6656 $response_body = wp_remote_retrieve_body($response);
6657 //error_log('[MXCHAT-EMBED] Received response length: ' . strlen($response_body) . ' bytes');
6658
6659 $response_data = json_decode($response_body, true);
6660
6661 if (json_last_error() !== JSON_ERROR_NONE) {
6662 $error = json_last_error_msg();
6663 //error_log('[MXCHAT-EMBED] JSON Parse Error: ' . $error);
6664 //error_log('[MXCHAT-EMBED] Response preview: ' . substr($response_body, 0, 200));
6665 return "Failed to parse API response: $error";
6666 }
6667
6668 // Handle different response formats based on provider
6669 if (strpos($selected_model, 'gemini-embedding') === 0) {
6670 // Gemini API response format
6671 if (isset($response_data['embedding']['values'])) {
6672 $embedding_dimensions = count($response_data['embedding']['values']);
6673 //error_log('[MXCHAT-EMBED] Successfully extracted Gemini embedding with ' . $embedding_dimensions . ' dimensions');
6674
6675 // Check if embedding dimensions are as expected (should be 1536)
6676 if ($embedding_dimensions !== 1536) {
6677 //error_log('[MXCHAT-EMBED] Warning: Unexpected Gemini embedding dimensions: ' . $embedding_dimensions);
6678 }
6679
6680 return $response_data['embedding']['values'];
6681 } else {
6682 //error_log('[MXCHAT-EMBED] Error: No embedding found in Gemini response');
6683 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
6684
6685 if (isset($response_data['error'])) {
6686 $error_message = "Gemini API Error in response: " . wp_json_encode($response_data['error']);
6687 //error_log('[MXCHAT-EMBED] ' . $error_message);
6688 return $error_message;
6689 }
6690
6691 $error_message = "Invalid Gemini API response format: No embedding found";
6692 //error_log('[MXCHAT-EMBED] ' . $error_message);
6693 return $error_message;
6694 }
6695 } else {
6696 // OpenAI/Voyage API response format
6697 if (isset($response_data['data'][0]['embedding'])) {
6698 $embedding_dimensions = count($response_data['data'][0]['embedding']);
6699 //error_log('[MXCHAT-EMBED] Successfully extracted embedding with ' . $embedding_dimensions . ' dimensions');
6700
6701 // Check if embedding dimensions are as expected
6702 if (($selected_model === 'text-embedding-ada-002' && $embedding_dimensions !== 1536) ||
6703 ($selected_model === 'voyage-3-large' && $embedding_dimensions !== 2048)) {
6704 //error_log('[MXCHAT-EMBED] Warning: Unexpected embedding dimensions');
6705 }
6706
6707 return $response_data['data'][0]['embedding'];
6708 } else {
6709 //error_log('[MXCHAT-EMBED] Error: No embedding found in response');
6710 //error_log('[MXCHAT-EMBED] Response structure: ' . wp_json_encode(array_keys($response_data)));
6711
6712 if (isset($response_data['error'])) {
6713 $error_message = "API Error in response: " . wp_json_encode($response_data['error']);
6714 //error_log('[MXCHAT-EMBED] ' . $error_message);
6715 return $error_message;
6716 }
6717
6718 $error_message = "Invalid API response format: No embedding found";
6719 //error_log('[MXCHAT-EMBED] ' . $error_message);
6720 return $error_message;
6721 }
6722 }
6723 }
6724
6725
6726
6727 }
6728 ?>
6729