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

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

738 lines 41.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit; // Exit if accessed directly
4 }
5
6 class MxChat_Public {
7 private $options;
8
9 public function __construct() {
10 // Simply get the options without defining duplicated defaults
11 $this->options = get_option('mxchat_options', array());
12 add_shortcode('mxchat_chatbot', array($this, 'render_chatbot_shortcode'));
13 add_action('wp_footer', array($this, 'append_chatbot_to_body'));
14 }
15
16 /**
17 * UPDATED: Enhanced should_hide_chatbot method - only blocks auto-append, not shortcodes
18 */
19 private function should_hide_chatbot($context = 'auto') {
20 global $post;
21
22 if (!$post) {
23 return false;
24 }
25
26 // Only hide if it's the auto-append context (floating="yes" from global setting)
27 // Allow shortcodes to work regardless of this setting
28 if ($context === 'auto') {
29 // Check new visibility field first
30 $visibility = get_post_meta($post->ID, '_mxchat_page_visibility', true);
31 if ($visibility === 'hide') {
32 return true;
33 }
34
35 // Backward compat: check legacy field if new field not set
36 if (empty($visibility)) {
37 $hide_chatbot = get_post_meta($post->ID, '_mxchat_hide_chatbot', true);
38 if ($hide_chatbot === '1') {
39 return true;
40 }
41 }
42 }
43
44 return false;
45 }
46
47 /**
48 * UPDATED: Enhanced append_chatbot_to_body method with context
49 */
50 public function append_chatbot_to_body() {
51 // Only run on public pages
52 if (is_admin() || wp_doing_ajax()) {
53 return;
54 }
55
56 // Check if auto-append chatbot should be hidden on this page
57 if ($this->should_hide_chatbot('auto')) {
58 return; // Don't show auto-appended chatbot
59 }
60
61 // Get the bot that should be displayed using new logic
62 $bot_to_show = $this->get_display_bot();
63
64 // Don't show chatbot if determination is false
65 if ($bot_to_show === false) {
66 return;
67 }
68
69 // Handle consent for display
70 $consent_category = 'marketing';
71 $has_consent = true;
72
73 if (
74 isset($this->options['complianz_toggle']) &&
75 $this->options['complianz_toggle'] === 'on' &&
76 function_exists('cmplz_has_consent')
77 ) {
78 $has_consent = cmplz_has_consent($consent_category);
79 }
80
81 // Display the appropriate chatbot (always floating for auto-append)
82 if ($bot_to_show && $bot_to_show !== 'default') {
83 // Show specific bot
84 echo do_shortcode('[mxchat_chatbot floating="yes" bot_id="' . esc_attr($bot_to_show) . '" has_consent="' . ($has_consent ? 'yes' : 'no') . '"]');
85 } else {
86 // Show default bot
87 echo do_shortcode('[mxchat_chatbot floating="yes" has_consent="' . ($has_consent ? 'yes' : 'no') . '"]');
88 }
89 }
90
91 private function mxchat_get_user_identifier() {
92 return sanitize_text_field($_SERVER['REMOTE_ADDR']);
93 }
94
95 /**
96 * UPDATED: Enhanced shortcode with context-aware hiding
97 */
98 public function render_chatbot_shortcode($atts) {
99 // UPDATED: Add bot_id parameter support and improve logic
100 $attributes = shortcode_atts(array(
101 'floating' => 'yes',
102 'has_consent' => 'yes',
103 'bot_id' => '' // Support for multi-bot functionality - empty means auto-detect
104 ), $atts);
105
106 // Determine which bot to use
107 $bot_id = $this->determine_bot_for_shortcode($attributes['bot_id']);
108
109 // UPDATED: Only check hiding for floating shortcodes that could conflict with auto-append
110 // Non-floating shortcodes should always work
111 if ($attributes['floating'] === 'yes') {
112 // For floating shortcodes, check if auto-append is hidden
113 // This prevents duplicate floating chatbots
114 if ($this->should_hide_chatbot('auto') && $this->is_auto_append_enabled()) {
115 // If auto-append is enabled but hidden on this page,
116 // allow the floating shortcode to work (user is overriding)
117 // But if auto-append is disabled globally, also allow shortcode
118 }
119 }
120
121 // Non-floating shortcodes (floating="no") should NEVER be blocked by the hide setting
122 // This allows embedded chatbots even when floating is hidden
123
124 $is_floating = $attributes['floating'] === 'yes';
125 $bot_id = sanitize_key($bot_id); // Sanitize bot ID
126
127 // Rest of your existing shortcode rendering logic continues unchanged...
128 // [All the existing HTML generation code remains the same]
129
130 // Get bot-specific options if multi-bot add-on is active
131 $bot_options = $this->get_bot_options($bot_id);
132
133 // Use bot-specific options or fall back to default options
134 $current_options = !empty($bot_options) ? $bot_options : $this->options;
135
136 // [Rest of your existing rendering code stays exactly the same]
137 // Just remove the old should_hide_chatbot() check from the beginning
138
139 // Check for Complianz consent if the toggle is enabled
140 $initial_visibility = 'hidden';
141 $additional_class = '';
142
143 if (isset($current_options['complianz_toggle']) && $current_options['complianz_toggle'] === 'on') {
144 $additional_class = ' no-consent';
145 }
146 $visibility_class = $initial_visibility . $additional_class;
147
148 $theme_options = get_option('mxchat_theme_options', array());
149 $custom_send_image = isset($theme_options['custom_send_button_image']) ? esc_url($theme_options['custom_send_button_image']) : '';
150 $send_width = isset($theme_options['send_button_width']) ? intval($theme_options['send_button_width']) : 24;
151 $send_height = isset($theme_options['send_button_height']) ? intval($theme_options['send_button_height']) : 24;
152 $send_rotation = isset($theme_options['send_button_rotation']) ? intval($theme_options['send_button_rotation']) : 0;
153
154 // Check if an AI theme is active (global or bot-specific) - if so, skip inline color styles
155 $ai_theme_active = !empty($theme_options['active_ai_theme_css']);
156 $bot_has_theme = isset($theme_options['bot_theme_assignments'][$bot_id]);
157 $skip_inline_colors = $ai_theme_active || $bot_has_theme;
158
159 // UPDATED: Use current_options instead of $this->options throughout
160 $bg_color = $current_options['chatbot_background_color'] ?? '#fff';
161 $user_message_bg_color = $current_options['user_message_bg_color'] ?? '#fff';
162 $user_message_font_color = $current_options['user_message_font_color'] ?? '#212121';
163 $bot_message_bg_color = $current_options['bot_message_bg_color'] ?? '#212121';
164 $bot_message_font_color = $current_options['bot_message_font_color'] ?? '#fff';
165 $top_bar_bg_color = $current_options['top_bar_bg_color'] ?? '#212121';
166 $send_button_font_color = $current_options['send_button_font_color'] ?? '#212121';
167 $intro_message = $current_options['intro_message'] ?? esc_html__('Hello! How can I assist you today?', 'mxchat');
168 $top_bar_title = $current_options['top_bar_title'] ?? esc_html__('MxChat: Basic', 'mxchat');
169 $chatbot_background_color = $current_options['chatbot_background_color'] ?? '#212121';
170 $icon_color = $current_options['icon_color'] ?? '#fff';
171 $chat_input_font_color = $current_options['chat_input_font_color'] ?? '#212121';
172 $close_button_color = $current_options['close_button_color'] ?? '#fff';
173 $chatbot_bg_color = $current_options['chatbot_bg_color'] ?? '#fff';
174 $pre_chat_message = isset($current_options['pre_chat_message']) ? sanitize_textarea_field(trim($current_options['pre_chat_message'])) : '';
175 $user_id = sanitize_key($this->mxchat_get_user_identifier());
176 $email_state = $this->determine_email_collection_state();
177 $show_email_form = $email_state['show_email_form'];
178 $user_email = $email_state['user_email'] ?? '';
179 $user_name = $email_state['user_name'] ?? '';
180 // Pre-chat dismissal now handled client-side via localStorage (zero server load)
181 $input_copy = isset($current_options['input_copy']) ? esc_attr($current_options['input_copy']) : esc_attr__('How can I assist?', 'mxchat');
182 $rate_limit_message = isset($current_options['rate_limit_message']) ? esc_attr($current_options['rate_limit_message']) : esc_attr__('Rate limit exceeded. Please try again later.', 'mxchat');
183 $mode_indicator_bg_color = $current_options['mode_indicator_bg_color'] ?? '#212121';
184 $mode_indicator_font_color = $current_options['mode_indicator_font_color'] ?? '#fff';
185 $quick_questions_toggle_color = $current_options['quick_questions_toggle_color'] ?? '#212121';
186
187 $privacy_toggle = isset($current_options['privacy_toggle']) && $current_options['privacy_toggle'] === 'on';
188 $privacy_text = isset($current_options['privacy_text']) ? wp_kses_post($current_options['privacy_text']) : wp_kses_post(__('By chatting, you agree to our <a href="https://example.com/privacy-policy" target="_blank">privacy policy</a>.', 'mxchat'));
189
190 $popular_question_1 = isset($current_options['popular_question_1']) ? esc_html($current_options['popular_question_1']) : '';
191 $popular_question_2 = isset($current_options['popular_question_2']) ? esc_html($current_options['popular_question_2']) : '';
192 $popular_question_3 = isset($current_options['popular_question_3']) ? esc_html($current_options['popular_question_3']) : '';
193 $additional_questions = isset($current_options['additional_popular_questions']) ? $current_options['additional_popular_questions'] : [];
194 $custom_icon = isset($current_options['custom_icon']) ? esc_url($current_options['custom_icon']) : '';
195 $title_icon = isset($current_options['title_icon']) ? esc_url($current_options['title_icon']) : '';
196 // AI agent text - if explicitly set to empty string, hide the indicator entirely
197 $ai_agent_text = isset($current_options['ai_agent_text']) ? $current_options['ai_agent_text'] : __('AI Agent', 'mxchat');
198
199 $live_agent_message_bg_color = $current_options['live_agent_message_bg_color'] ?? '#212121';
200 $live_agent_message_font_color = $current_options['live_agent_message_font_color'] ?? '#fff';
201 $enable_email_block = isset($current_options['enable_email_block']) &&
202 ($current_options['enable_email_block'] === '1' || $current_options['enable_email_block'] === 'on');
203
204 // Add name field variables
205 $enable_name_field = isset($current_options['enable_name_field']) &&
206 ($current_options['enable_name_field'] === '1' || $current_options['enable_name_field'] === 'on');
207 $name_field_placeholder = isset($current_options['name_field_placeholder']) ?
208 esc_attr($current_options['name_field_placeholder']) :
209 esc_attr__('Enter your name', 'mxchat');
210
211 ob_start();
212
213 // Check if floating attribute is set to 'yes' and wrap accordingly
214 if ($is_floating) {
215 echo '<div id="floating-chatbot-' . esc_attr($bot_id) . '" class="floating-chatbot ' . $initial_visibility . $additional_class . '">';
216 }
217
218 // Add bot_id to the chatbot wrapper as a data attribute
219 echo '<div id="mxchat-chatbot-wrapper-' . esc_attr($bot_id) . '" class="mxchat-chatbot-wrapper" data-bot-id="' . esc_attr($bot_id) . '">';
220
221 echo ' <div class="chatbot-top-bar" id="exit-chat-button-' . esc_attr($bot_id) . '"' . ($skip_inline_colors ? '' : ' style="background: ' . esc_attr($top_bar_bg_color) . ';"') . '>';
222 echo ' <div class="chatbot-title-container">';
223 echo ' <div class="chatbot-title-group">';
224 if (!empty($title_icon)) {
225 echo ' <img src="' . esc_url($title_icon) . '" alt="" class="chatbot-title-icon">';
226 }
227 echo ' <p class="chatbot-title"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($close_button_color) . ';"') . '>' . esc_html($top_bar_title) . '</p>';
228 echo ' </div>';
229 // Only show mode indicator if ai_agent_text is not empty
230 if (!empty(trim($ai_agent_text))) {
231 echo '<span class="chat-mode-indicator" id="chat-mode-indicator-' . esc_attr($bot_id) . '" data-ai-text="' . esc_attr($ai_agent_text) . '"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($mode_indicator_font_color) . '; background-color: ' . esc_attr($mode_indicator_bg_color) . ';"') . '>' . esc_html($ai_agent_text) . '</span>';
232 }
233 echo ' </div>';
234 // Overflow menu (3-dot) trigger + dropdown container.
235 // Sibling of .exit-chat, rendered to its left. JS hides the trigger
236 // if no menu items are enabled; outer click + Escape close the menu.
237 echo ' <div class="mxchat-header-menu-wrap" data-bot-id="' . esc_attr($bot_id) . '"' . ($skip_inline_colors ? '' : ' style="--mxchat-menu-bg: ' . esc_attr($bot_message_bg_color) . '; --mxchat-menu-fg: ' . esc_attr($bot_message_font_color) . ';"') . '>';
238 echo ' <button class="mxchat-header-btn mxchat-menu-trigger" type="button" aria-haspopup="menu" aria-expanded="false" aria-label="' . esc_attr__('More options', 'mxchat') . '"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($close_button_color) . ';"') . '>';
239 echo ' <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">';
240 echo ' <circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/>';
241 echo ' </svg>';
242 echo ' </button>';
243 echo ' <div class="mxchat-header-menu" role="menu" aria-hidden="true" hidden></div>';
244 echo ' </div>';
245 echo ' <button class="exit-chat" type="button" aria-label="' . esc_attr__('Close', 'mxchat') . '"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($close_button_color) . ';"') . '>';
246 echo ' <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" id="ic-close" aria-hidden="true">';
247 echo ' <line x1="6" y1="6" x2="18" y2="18"></line>';
248 echo ' <line x1="18" y1="6" x2="6" y2="18"></line>';
249 echo ' </svg>';
250 echo ' <span>' . esc_html__('Close', 'mxchat') . '</span>';
251 echo ' </button>';
252 echo ' </div>';
253
254 // 3b) Main chatbot container
255 echo ' <div id="mxchat-chatbot-' . esc_attr($bot_id) . '" class="mxchat-chatbot"' . ($skip_inline_colors ? '' : ' style="background-color: ' . esc_attr($chatbot_bg_color) . ';"') . '>';
256
257 if ($enable_email_block) {
258 echo '<div id="email-blocker-' . esc_attr($bot_id) . '" class="email-blocker" style="' . ($show_email_form ? '' : 'display: none;') . '">';
259 echo ' <div class="email-blocker-header">';
260
261 $header_html = isset($current_options['email_blocker_header_content'])
262 ? $current_options['email_blocker_header_content']
263 : '';
264 echo wp_kses_post($header_html);
265
266 echo ' </div>';
267 echo ' <form id="email-collection-form-' . esc_attr($bot_id) . '" class="email-collection-form" method="POST" action="">';
268
269 // Add name field if enabled
270 if ($enable_name_field) {
271 echo ' <label for="user-name-' . esc_attr($bot_id) . '" class="sr-only mxchat-name-label">' . esc_html__('Name', 'mxchat') . '</label>';
272 echo ' <input type="text" id="user-name-' . esc_attr($bot_id) . '" name="user_name" class="mxchat-name-input" required placeholder="' . $name_field_placeholder . '" />';
273 }
274
275 echo ' <label for="user-email-' . esc_attr($bot_id) . '" class="sr-only">' . esc_html__('Email Address', 'mxchat') . '</label>';
276 echo ' <input type="email" id="user-email-' . esc_attr($bot_id) . '" name="user_email" class="mxchat-email-input" required placeholder="' . esc_attr__('Enter your email address', 'mxchat') . '" />';
277 echo '<button type="submit" id="email-submit-button-' . esc_attr($bot_id) . '" class="email-submit-button">';
278 $button_text = isset($current_options['email_blocker_button_text'])
279 ? $current_options['email_blocker_button_text']
280 : esc_html__('Start Chat', 'mxchat');
281 echo esc_html($button_text);
282 echo '</button>';
283 echo ' </form>';
284 echo '</div>';
285 }
286
287 echo ' <div id="mxchat-init-loader-' . esc_attr($bot_id) . '" class="mxchat-init-loader" style="display:none;">';
288 echo ' <div class="mxchat-init-loader-dots">';
289 echo ' <span class="dot"></span><span class="dot"></span><span class="dot"></span>';
290 echo ' </div>';
291 echo ' </div>';
292
293 echo ' <div id="chat-container-' . esc_attr($bot_id) . '" class="chat-container" style="' . ($enable_email_block && $show_email_form ? 'display: none;' : '') . '">';
294 echo ' <div id="chat-box-' . esc_attr($bot_id) . '" class="chat-box">';
295 echo ' <div class="bot-message"' . ($skip_inline_colors ? '' : ' style="background: ' . esc_attr($bot_message_bg_color) . ';"') . '>';
296 echo ' <div dir="auto"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($bot_message_font_color) . ';"') . '>';
297 echo wp_kses_post($intro_message);
298 echo ' </div>';
299 echo ' </div>';
300 echo ' </div>'; // end #chat-box
301
302
303 // Replace the existing popular questions section with this:
304 echo ' <div id="mxchat-popular-questions-' . esc_attr($bot_id) . '" class="mxchat-popular-questions">';
305 echo ' <div class="mxchat-popular-questions-container">';
306
307 // Collapse button (down arrow) - shows when open, centered at top
308 echo ' <button class="questions-collapse-btn" aria-label="' . esc_attr__('Hide Quick Questions', 'mxchat') . '">';
309 echo ' <svg width="25" height="25" viewBox="0 0 24 24" fill="none"' . ($skip_inline_colors ? '' : ' stroke="' . esc_attr($quick_questions_toggle_color) . '"') . ' stroke-width="2">';
310 echo ' <polyline points="6,9 12,15 18,9"></polyline>';
311 echo ' </svg>';
312 echo ' </button>';
313
314 // Expand button (up arrow) - shows when collapsed
315 echo ' <button class="questions-toggle-btn" aria-label="' . esc_attr__('Show Quick Questions', 'mxchat') . '">';
316 echo ' <svg width="25" height="25" viewBox="0 0 24 24" fill="none"' . ($skip_inline_colors ? '' : ' stroke="' . esc_attr($quick_questions_toggle_color) . '"') . ' stroke-width="2">';
317 echo ' <polyline points="18,15 12,9 6,15"></polyline>';
318 echo ' </svg>';
319 echo ' </button>';
320
321 if (!empty($popular_question_1)) {
322 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_1) . '</button>';
323 }
324 if (!empty($popular_question_2)) {
325 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_2) . '</button>';
326 }
327 if (!empty($popular_question_3)) {
328 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_3) . '</button>';
329 }
330
331 if (!empty($additional_questions) && is_array($additional_questions)) {
332 foreach ($additional_questions as $index => $question) {
333 if (!empty($question)) {
334 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($question) . '</button>';
335 }
336 }
337 }
338
339 echo ' </div>';
340 echo ' </div>';
341
342 echo ' <div id="input-container-' . esc_attr($bot_id) . '" class="input-container">';
343 echo ' <textarea id="chat-input-' . esc_attr($bot_id) . '" class="chat-input" dir="auto" placeholder="' . esc_attr($input_copy) . '"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($chat_input_font_color) . ';"') . '></textarea>';
344 echo ' <button id="send-button-' . esc_attr($bot_id) . '" class="send-button" aria-label="' . esc_attr__('Send message', 'mxchat') . '">';
345 if (!empty($custom_send_image)) {
346 echo ' <img src="' . esc_url($custom_send_image) . '" alt="' . esc_attr__('Send', 'mxchat') . '" style="width: ' . intval($send_width) . 'px; height: ' . intval($send_height) . 'px; transform: rotate(' . intval($send_rotation) . 'deg);" />';
347 } else {
348 $send_svg_style = 'width: ' . intval($send_width) . 'px; height: ' . intval($send_height) . 'px; transform: rotate(' . intval($send_rotation) . 'deg);';
349 if (!$skip_inline_colors) {
350 $send_svg_style = 'fill: ' . esc_attr($send_button_font_color) . '; ' . $send_svg_style;
351 }
352 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="' . $send_svg_style . '">';
353 echo ' <path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480V396.4c0-4 1.5-7.8 4.2-10.7L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z"></path>';
354 echo ' </svg>';
355 }
356 echo ' </button>';
357 echo ' </div>';
358
359 echo ' <div class="chat-toolbar">';
360
361 // PDF Upload Button - wrapped in conditional using current_options
362 $show_pdf_button = isset($current_options['show_pdf_upload_button']) ? $current_options['show_pdf_upload_button'] : 'on';
363 if ($show_pdf_button === 'on') {
364 echo ' <input type="file" id="pdf-upload-' . esc_attr($bot_id) . '" class="pdf-upload" accept=".pdf" style="display: none;">';
365 echo ' <button id="pdf-upload-btn-' . esc_attr($bot_id) . '" class="toolbar-btn pdf-upload-btn" title="' . esc_attr__('Upload PDF', 'mxchat') . '">';
366 echo ' <!-- Icon from Font Awesome Free: https://fontawesome.com/license/free -->';
367 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" stroke="currentColor">';
368 echo ' <path d="M64 464l48 0 0 48-48 0c-35.3 0-64-28.7-64-64L0 64C0 28.7 28.7 0 64 0L229.5 0c17 0 33.3 6.7 45.3 18.7l90.5 90.5c12 12 18.7 28.3 18.7 45.3L384 304l-48 0 0-144-80 0c-17.7 0-32-14.3-32-32l0-80L64 48c-8.8 0-16 7.2-16 16l0 384c0 8.8 7.2 16 16 16zM176 352l32 0c30.9 0 56 25.1 56 56s-25.1 56-56 56l-16 0 0 32c0 8.8-7.2 16-16 16s-16-7.2-16-16l0-48 0-80c0-8.8 7.2-16 16-16zm32 80c13.3 0 24-10.7 24-24s-10.7-24-24-24l-16 0 0 48 16 0zm96-80l32 0c26.5 0 48 21.5 48 48l0 64c0 26.5-21.5 48-48 48l-32 0c-8.8 0-16-7.2-16-16l0-128c0-8.8 7.2-16 16-16zm32 128c8.8 0 16-7.2 16-16l0-64c0-8.8-7.2-16-16-16l-16 0 0 96 16 0zm80-112c0-8.8 7.2-16 16-16l48 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0 0 32 32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0 0 48c0 8.8-7.2 16-16 16s-16-7.2-16-16l0-64 0-64z"></path>';
369 echo ' </svg>';
370 echo ' </button>';
371 }
372
373 // Word Upload Button - wrapped in conditional using current_options
374 $show_word_button = isset($current_options['show_word_upload_button']) ? $current_options['show_word_upload_button'] : 'on';
375 if ($show_word_button === 'on') {
376 echo ' <input type="file" id="word-upload-' . esc_attr($bot_id) . '" class="word-upload" accept=".docx" style="display: none;">';
377 echo ' <button id="word-upload-btn-' . esc_attr($bot_id) . '" class="toolbar-btn word-upload-btn" title="' . esc_attr__('Upload Word Document', 'mxchat') . '">';
378 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" stroke="currentColor">';
379 echo ' <path d="M48 448L48 64c0-8.8 7.2-16 16-16l160 0 0 80c0 17.7 14.3 32 32 32l80 0 0 288c0 8.8-7.2 16-16 16L64 464c-8.8 0-16-7.2-16-16zM64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-293.5c0-17-6.7-33.3-18.7-45.3L274.7 18.7C262.7 6.7 246.5 0 229.5 0L64 0zm55 241.1c-3.8-12.7-17.2-19.9-29.9-16.1s-19.9 17.2-16.1 29.9l48 160c3 10.2 12.4 17.1 23 17.1s19.9-7 23-17.1l25-83.4 25 83.4c3 10.2 12.4 17.1 23 17.1s19.9-7 23-17.1l48-160c3.8-12.7-3.4-26.1-16.1-29.9s-26.1 3.4-29.9 16.1l-25 83.4-25-83.4c-3-10.2-12.4-17.1-23-17.1s-19.9 7-23 17.1l-25 83.4-25-83.4z"/></svg>';
380 echo ' </button>';
381 }
382
383 // File containers
384 echo ' <div id="active-pdf-container-' . esc_attr($bot_id) . '" class="active-pdf-container" style="display: none;">';
385 echo ' <span id="active-pdf-name-' . esc_attr($bot_id) . '" class="active-pdf-name"></span>';
386 echo ' <button id="remove-pdf-btn-' . esc_attr($bot_id) . '" class="remove-pdf-btn" title="' . esc_attr__('Remove PDF', 'mxchat') . '">';
387 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">';
388 echo ' <line x1="18" y1="6" x2="6" y2="18"></line>';
389 echo ' <line x1="6" y1="6" x2="18" y2="18"></line>';
390 echo ' </svg>';
391 echo ' </button>';
392 echo ' </div>';
393
394 echo ' <div id="active-word-container-' . esc_attr($bot_id) . '" class="active-word-container" style="display: none;">';
395 echo ' <span id="active-word-name-' . esc_attr($bot_id) . '" class="active-word-name"></span>';
396 echo ' <button id="remove-word-btn-' . esc_attr($bot_id) . '" class="remove-word-btn" title="' . esc_attr__('Remove Word Document', 'mxchat') . '">';
397 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">';
398 echo ' <line x1="18" y1="6" x2="6" y2="18"></line>';
399 echo ' <line x1="6" y1="6" x2="18" y2="18"></line>';
400 echo ' </svg>';
401 echo ' </button>';
402 echo ' </div>';
403
404 // Perplexity Button
405 if (apply_filters('mxchat_perplexity_should_show_logo', true)) {
406 echo ' <button id="perplexity-search-btn-' . esc_attr($bot_id) . '" class="toolbar-btn perplexity-search-btn" title="' . esc_attr__('Search with Perplexity', 'mxchat-perplexity') . '">';
407 echo ' <svg fill="currentColor" height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">';
408 echo ' <path d="M19.785 0v7.272H22.5V17.62h-2.935V24l-7.037-6.194v6.145h-1.091v-6.152L4.392 24v-6.465H1.5V7.188h2.884V0l7.053 6.494V.19h1.09v6.49L19.786 0zm-7.257 9.044v7.319l5.946 5.234V14.44l-5.946-5.397zm-1.099-.08l-5.946 5.398v7.235l5.946-5.234V8.965zm8.136 7.58h1.844V8.349H13.46l6.105 5.54v2.655zm-8.982-8.28H2.59v8.195h1.8v-2.576l6.192-5.62zM5.475 2.476v4.71h5.115l-5.115-4.71zm13.219 0l-5.115 4.71h5.115v-4.71z"></path>';
409 echo ' </svg>';
410 echo ' </button>';
411 }
412
413
414 // Image Analysis Button - hidden by default, controlled by MxChat Vision add-on
415 echo ' <input type="file" id="image-upload-' . esc_attr($bot_id) . '" class="image-upload" accept="image/*" style="display: none;" multiple>';
416 echo ' <button id="image-upload-btn-' . esc_attr($bot_id) . '" class="toolbar-btn image-upload-btn" title="' . esc_attr__('Upload Image for Analysis', 'mxchat') . '" style="display: none;">';
417 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="fill: none !important;">';
418 echo ' <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" style="fill: none !important;"/>';
419 echo ' <circle cx="12" cy="13" r="3" style="fill: none !important;"/>';
420 echo ' </svg>';
421 echo ' </button>';
422
423
424 echo ' </div>';
425
426 echo ' <div class="chatbot-footer">';
427
428 // Output the privacy notice if enabled
429 if ($privacy_toggle && !empty($privacy_text)) {
430 echo '<p class="privacy-notice">' . $privacy_text . '</p>';
431 }
432
433 echo ' </div>';
434 echo ' </div>';
435 echo ' </div>';
436 echo '</div>';
437
438 if ($is_floating) {
439 echo '</div>';
440
441 if (!empty($pre_chat_message)) {
442 // Rendered hidden by default — JS checkPreChatDismissal() handles show/hide via localStorage
443 echo '<div id="pre-chat-message-' . esc_attr($bot_id) . '" class="pre-chat-message" style="display:none;">';
444 echo nl2br(esc_html($pre_chat_message));
445 echo '<button class="close-pre-chat-message" aria-label="' . esc_attr__('Close', 'mxchat') . '">&times;</button>';
446 echo '</div>';
447 }
448
449 echo '<div class="floating-chatbot-button ' . esc_attr($visibility_class) . '" id="floating-chatbot-button-' . esc_attr($bot_id) . '"' . ($skip_inline_colors ? '' : ' style="background: ' . esc_attr($chatbot_background_color) . '; color: ' . esc_attr($send_button_font_color) . ';"') . '>';
450 echo '<div id="chat-notification-badge-' . esc_attr($bot_id) . '" class="chat-notification-badge" style="display: none; position: absolute; top: -8px; right: -8px; background-color: #ff4444; color: white; border-radius: 50%; padding: 4px 8px; font-size: 12px; font-weight: bold; z-index: 10001;">1</div>';
451
452 if (!empty($custom_icon)) {
453 echo '<img src="' . $custom_icon . '" alt="' . esc_attr__('Chatbot Icon', 'mxchat') . '" style="height: 48px; width: 48px; object-fit: contain;" />';
454 } else {
455 $widget_icon_style = 'height: 48px; width: 48px;';
456 if (!$skip_inline_colors) {
457 $widget_icon_style .= ' fill: ' . esc_attr($icon_color) . ';';
458 }
459 echo '<svg id="widget_icon_10" style="' . $widget_icon_style . '" viewBox="0 0 1120 1120" fill="none" xmlns="http://www.w3.org/2000/svg">';
460 echo ' <path fill-rule="evenodd" clip-rule="evenodd" d="M252 434C252 372.144 302.144 322 364 322H770C831.856 322 882 372.144 882 434V614.459L804.595 585.816C802.551 585.06 800.94 583.449 800.184 581.405L763.003 480.924C760.597 474.424 751.403 474.424 748.997 480.924L711.816 581.405C711.06 583.449 709.449 585.06 707.405 585.816L606.924 622.997C600.424 625.403 600.424 634.597 606.924 637.003L707.405 674.184C709.449 674.94 711.06 676.551 711.816 678.595L740.459 756H629.927C629.648 756.476 629.337 756.945 628.993 757.404L578.197 825.082C572.597 832.543 561.403 832.543 555.803 825.082L505.007 757.404C504.663 756.945 504.352 756.476 504.073 756H364C302.144 756 252 705.856 252 644V434ZM633.501 471.462C632.299 468.212 627.701 468.212 626.499 471.462L619.252 491.046C618.874 492.068 618.068 492.874 617.046 493.252L597.462 500.499C594.212 501.701 594.212 506.299 597.462 507.501L617.046 514.748C618.068 515.126 618.874 515.932 619.252 516.954L626.499 536.538C627.701 539.788 632.299 539.788 633.501 536.538L640.748 516.954C641.126 515.932 641.932 515.126 642.954 514.748L662.538 507.501C665.788 506.299 665.788 501.701 662.538 500.499L642.954 493.252C641.932 492.874 641.126 492.068 640.748 491.046L633.501 471.462Z" ></path>';
461 echo ' <path d="M771.545 755.99C832.175 755.17 881.17 706.175 881.99 645.545L804.595 674.184C802.551 674.94 800.94 676.551 800.184 678.595L771.545 755.99Z" ></path>';
462 echo '</svg>';
463 }
464 echo '</div>';
465 }
466
467 return ob_get_clean();
468 }
469
470 /**
471 * Get bot-specific options for multi-bot functionality
472 * Falls back to default options if bot_id is 'default' or multi-bot add-on is not active
473 */
474 private function get_bot_options($bot_id = 'default') {
475 // If default bot or multi-bot add-on not active, return empty (use default options)
476 if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
477 return array();
478 }
479
480 //Hook for multi-bot add-on to provide bot-specific options
481 $bot_options = apply_filters('mxchat_get_bot_options', array(), $bot_id);
482
483 return is_array($bot_options) ? $bot_options : array();
484 }
485
486 /**
487 * Get bot-specific Pinecone configuration
488 * Used in the knowledge retrieval functions
489 */
490 private function get_bot_pinecone_config($bot_id = 'default') {
491 // If default bot or multi-bot add-on not active, use default Pinecone config
492 if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
493 $addon_options = get_option('mxchat_pinecone_addon_options', array());
494 return array(
495 'use_pinecone' => (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1'),
496 'api_key' => $addon_options['mxchat_pinecone_api_key'] ?? '',
497 'host' => $addon_options['mxchat_pinecone_host'] ?? '',
498 'namespace' => $addon_options['mxchat_pinecone_namespace'] ?? ''
499 );
500 }
501
502 // Hook for multi-bot add-on to provide bot-specific Pinecone config
503 $bot_pinecone_config = apply_filters('mxchat_get_bot_pinecone_config', array(), $bot_id);
504
505 return is_array($bot_pinecone_config) ? $bot_pinecone_config : array();
506 }
507
508
509 private function determine_email_collection_state() {
510 // Logged-in users skip the email form
511 if (is_user_logged_in()) {
512 $current_user = wp_get_current_user();
513 return [
514 'show_email_form' => false,
515 'user_email' => $current_user->user_email,
516 'user_name' => $current_user->display_name ?: $current_user->first_name ?: ''
517 ];
518 }
519
520 // For guests, default to showing the email form.
521 // The session-based check happens client-side via AJAX since the
522 // session ID lives in the browser cookie/localStorage.
523 return [
524 'show_email_form' => true,
525 'user_email' => '',
526 'user_name' => ''
527 ];
528 }
529
530
531 /**
532 * NEW: Determine if and which chatbot should be displayed
533 */
534 private function get_display_bot() {
535 // Get page-specific settings from meta box
536 $page_setting = $this->get_page_bot_setting();
537
538 // Get global settings - FIXED: Check for 'on' instead of 'on'
539 $global_autoshow = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on';
540 $global_default_bot = isset($this->options['default_bot']) ? $this->options['default_bot'] : 'default';
541
542 // If page specifically hides chatbot, don't show anything
543 if ($page_setting && $page_setting['action'] === 'hide') {
544 return false;
545 }
546
547 // If page specifies a specific bot, use that
548 if ($page_setting && $page_setting['action'] === 'show') {
549 return $page_setting['bot_id'];
550 }
551
552 // Page setting is 'global' or no page setting exists
553 // Check global auto-show setting
554 if ($global_autoshow) {
555 // Check post type visibility settings
556 if (!$this->should_show_on_current_post_type()) {
557 return false;
558 }
559
560 // Global auto-show is enabled, return the default bot
561 return $global_default_bot;
562 }
563
564 // Global auto-show is disabled and no page-specific bot selected
565 // Don't show chatbot (user should use shortcodes)
566 return false;
567 }
568
569 /**
570 * Check if chatbot should be shown on the current post type
571 */
572 private function should_show_on_current_post_type() {
573 // Get visibility settings
574 $mode = isset($this->options['post_type_visibility_mode']) ? $this->options['post_type_visibility_mode'] : 'all';
575 $list = isset($this->options['post_type_visibility_list']) ? $this->options['post_type_visibility_list'] : array();
576
577 // Ensure list is an array
578 if (!is_array($list)) {
579 $list = array();
580 }
581
582 // If mode is 'all', show on all post types
583 if ($mode === 'all') {
584 return true;
585 }
586
587 // Get current post type
588 $current_post_type = $this->get_current_post_type();
589
590 // If we can't determine post type, default to showing
591 if (empty($current_post_type)) {
592 return true;
593 }
594
595 // Check based on mode
596 if ($mode === 'include') {
597 // Only show on selected post types
598 return in_array($current_post_type, $list);
599 } elseif ($mode === 'exclude') {
600 // Hide on selected post types
601 return !in_array($current_post_type, $list);
602 }
603
604 // Default to showing
605 return true;
606 }
607
608 /**
609 * Get the current post type
610 */
611 private function get_current_post_type() {
612 // Try to get from queried object first
613 $queried_object = get_queried_object();
614
615 if ($queried_object instanceof WP_Post) {
616 return $queried_object->post_type;
617 }
618
619 // Try get_post_type()
620 $post_type = get_post_type();
621 if ($post_type) {
622 return $post_type;
623 }
624
625 // Check if we're on an archive
626 if (is_post_type_archive()) {
627 return get_query_var('post_type');
628 }
629
630 // Check common archive types
631 if (is_home() || is_single()) {
632 return 'post';
633 }
634
635 if (is_page()) {
636 return 'page';
637 }
638
639 return '';
640 }
641
642 /**
643 * Get page-specific bot setting using new visibility field with backward compat
644 */
645 private function get_page_bot_setting($post_id = null) {
646 if (!$post_id) {
647 $post_id = get_the_ID();
648 }
649
650 if (!$post_id) {
651 return null;
652 }
653
654 // Check new visibility field first
655 $visibility = get_post_meta($post_id, '_mxchat_page_visibility', true);
656
657 if ($visibility === 'hide') {
658 return array('action' => 'hide');
659 }
660
661 if ($visibility === 'show') {
662 $selected_bot = get_post_meta($post_id, '_mxchat_selected_bot', true);
663 $bot_id = !empty($selected_bot) ? $selected_bot : 'default';
664 return array('action' => 'show', 'bot_id' => $bot_id);
665 }
666
667 // Backward compat: check legacy hide checkbox if no new field set
668 if (empty($visibility)) {
669 $hide_chatbot = get_post_meta($post_id, '_mxchat_hide_chatbot', true);
670 if ($hide_chatbot === '1') {
671 return array('action' => 'hide');
672 }
673 }
674
675 // Check if specific bot is selected (legacy path)
676 $selected_bot = get_post_meta($post_id, '_mxchat_selected_bot', true);
677 if (!empty($selected_bot)) {
678 return array('action' => 'show', 'bot_id' => $selected_bot);
679 }
680
681 // Use global setting
682 return array('action' => 'global');
683 }
684
685 /**
686 * NEW: Determine which bot to use for shortcode
687 */
688 private function determine_bot_for_shortcode($shortcode_bot_id) {
689 // If bot_id explicitly provided in shortcode, use that
690 if (!empty($shortcode_bot_id)) {
691 return $shortcode_bot_id;
692 }
693
694 // No bot_id in shortcode, check page setting
695 $page_setting = $this->get_page_bot_setting();
696 if ($page_setting && $page_setting['action'] === 'show') {
697 return $page_setting['bot_id'];
698 }
699
700 // Fall back to default
701 return 'default';
702 }
703
704 /**
705 * NEW: Helper to check if auto-append is enabled globally
706 */
707 private function is_auto_append_enabled() {
708 return isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on';
709 }
710
711 /**
712 * UPDATED: Debug function with new context info
713 */
714
715 /**
716 * NEW: Helper method to get available bots (for admin notices, etc.)
717 */
718 private function get_available_bots() {
719 $bots = array('default' => __('Default Bot', 'mxchat'));
720
721 // Check if multi-bot addon is active
722 if (class_exists('MxChat_Multi_Bot_Manager')) {
723 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
724 $available_bots = $multi_bot_manager->get_available_bots();
725
726 // Add the available bots
727 foreach ($available_bots as $bot_id => $bot_name) {
728 $bots[$bot_id] = $bot_name;
729 }
730 }
731
732 return $bots;
733 }
734
735
736
737 }
738 ?>