PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.1
MxChat – AI Chatbot & Content Generation for WordPress v3.2.1
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.1, at includes/class-mxchat-public.php

726 lines 40.5 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 echo ' <button class="exit-chat" type="button" aria-label="' . esc_attr__('Minimize', 'mxchat') . '"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($close_button_color) . ';"') . '>';
235 echo ' <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" id="ic-minimize"' . ($skip_inline_colors ? '' : ' style="fill: ' . esc_attr($close_button_color) . ';"') . '>';
236 echo ' <path d="M11.67 3.87L9.9 2.1 0 12l9.9 9.9 1.77-1.77L3.54 12z"></path>';
237 echo ' </svg>';
238 echo ' <span>' . esc_html__('Minimize', 'mxchat') . '</span>';
239 echo ' </button>';
240 echo ' </div>';
241
242 // 3b) Main chatbot container
243 echo ' <div id="mxchat-chatbot-' . esc_attr($bot_id) . '" class="mxchat-chatbot"' . ($skip_inline_colors ? '' : ' style="background-color: ' . esc_attr($chatbot_bg_color) . ';"') . '>';
244
245 if ($enable_email_block) {
246 echo '<div id="email-blocker-' . esc_attr($bot_id) . '" class="email-blocker" style="' . ($show_email_form ? '' : 'display: none;') . '">';
247 echo ' <div class="email-blocker-header">';
248
249 $header_html = isset($current_options['email_blocker_header_content'])
250 ? $current_options['email_blocker_header_content']
251 : '';
252 echo wp_kses_post($header_html);
253
254 echo ' </div>';
255 echo ' <form id="email-collection-form-' . esc_attr($bot_id) . '" class="email-collection-form" method="POST" action="">';
256
257 // Add name field if enabled
258 if ($enable_name_field) {
259 echo ' <label for="user-name-' . esc_attr($bot_id) . '" class="sr-only mxchat-name-label">' . esc_html__('Name', 'mxchat') . '</label>';
260 echo ' <input type="text" id="user-name-' . esc_attr($bot_id) . '" name="user_name" class="mxchat-name-input" required placeholder="' . $name_field_placeholder . '" />';
261 }
262
263 echo ' <label for="user-email-' . esc_attr($bot_id) . '" class="sr-only">' . esc_html__('Email Address', 'mxchat') . '</label>';
264 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') . '" />';
265 echo '<button type="submit" id="email-submit-button-' . esc_attr($bot_id) . '" class="email-submit-button">';
266 $button_text = isset($current_options['email_blocker_button_text'])
267 ? $current_options['email_blocker_button_text']
268 : esc_html__('Start Chat', 'mxchat');
269 echo esc_html($button_text);
270 echo '</button>';
271 echo ' </form>';
272 echo '</div>';
273 }
274
275 echo ' <div id="mxchat-init-loader-' . esc_attr($bot_id) . '" class="mxchat-init-loader" style="display:none;">';
276 echo ' <div class="mxchat-init-loader-dots">';
277 echo ' <span class="dot"></span><span class="dot"></span><span class="dot"></span>';
278 echo ' </div>';
279 echo ' </div>';
280
281 echo ' <div id="chat-container-' . esc_attr($bot_id) . '" class="chat-container" style="' . ($enable_email_block && $show_email_form ? 'display: none;' : '') . '">';
282 echo ' <div id="chat-box-' . esc_attr($bot_id) . '" class="chat-box">';
283 echo ' <div class="bot-message"' . ($skip_inline_colors ? '' : ' style="background: ' . esc_attr($bot_message_bg_color) . ';"') . '>';
284 echo ' <div dir="auto"' . ($skip_inline_colors ? '' : ' style="color: ' . esc_attr($bot_message_font_color) . ';"') . '>';
285 echo wp_kses_post($intro_message);
286 echo ' </div>';
287 echo ' </div>';
288 echo ' </div>'; // end #chat-box
289
290
291 // Replace the existing popular questions section with this:
292 echo ' <div id="mxchat-popular-questions-' . esc_attr($bot_id) . '" class="mxchat-popular-questions">';
293 echo ' <div class="mxchat-popular-questions-container">';
294
295 // Collapse button (down arrow) - shows when open, centered at top
296 echo ' <button class="questions-collapse-btn" aria-label="' . esc_attr__('Hide Quick Questions', 'mxchat') . '">';
297 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">';
298 echo ' <polyline points="6,9 12,15 18,9"></polyline>';
299 echo ' </svg>';
300 echo ' </button>';
301
302 // Expand button (up arrow) - shows when collapsed
303 echo ' <button class="questions-toggle-btn" aria-label="' . esc_attr__('Show Quick Questions', 'mxchat') . '">';
304 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">';
305 echo ' <polyline points="18,15 12,9 6,15"></polyline>';
306 echo ' </svg>';
307 echo ' </button>';
308
309 if (!empty($popular_question_1)) {
310 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_1) . '</button>';
311 }
312 if (!empty($popular_question_2)) {
313 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_2) . '</button>';
314 }
315 if (!empty($popular_question_3)) {
316 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($popular_question_3) . '</button>';
317 }
318
319 if (!empty($additional_questions) && is_array($additional_questions)) {
320 foreach ($additional_questions as $index => $question) {
321 if (!empty($question)) {
322 echo '<button class="mxchat-popular-question" dir="auto">' . esc_html($question) . '</button>';
323 }
324 }
325 }
326
327 echo ' </div>';
328 echo ' </div>';
329
330 echo ' <div id="input-container-' . esc_attr($bot_id) . '" class="input-container">';
331 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>';
332 echo ' <button id="send-button-' . esc_attr($bot_id) . '" class="send-button" aria-label="' . esc_attr__('Send message', 'mxchat') . '">';
333 if (!empty($custom_send_image)) {
334 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);" />';
335 } else {
336 $send_svg_style = 'width: ' . intval($send_width) . 'px; height: ' . intval($send_height) . 'px; transform: rotate(' . intval($send_rotation) . 'deg);';
337 if (!$skip_inline_colors) {
338 $send_svg_style = 'fill: ' . esc_attr($send_button_font_color) . '; ' . $send_svg_style;
339 }
340 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="' . $send_svg_style . '">';
341 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>';
342 echo ' </svg>';
343 }
344 echo ' </button>';
345 echo ' </div>';
346
347 echo ' <div class="chat-toolbar">';
348
349 // PDF Upload Button - wrapped in conditional using current_options
350 $show_pdf_button = isset($current_options['show_pdf_upload_button']) ? $current_options['show_pdf_upload_button'] : 'on';
351 if ($show_pdf_button === 'on') {
352 echo ' <input type="file" id="pdf-upload-' . esc_attr($bot_id) . '" class="pdf-upload" accept=".pdf" style="display: none;">';
353 echo ' <button id="pdf-upload-btn-' . esc_attr($bot_id) . '" class="toolbar-btn pdf-upload-btn" title="' . esc_attr__('Upload PDF', 'mxchat') . '">';
354 echo ' <!-- Icon from Font Awesome Free: https://fontawesome.com/license/free -->';
355 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" stroke="currentColor">';
356 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>';
357 echo ' </svg>';
358 echo ' </button>';
359 }
360
361 // Word Upload Button - wrapped in conditional using current_options
362 $show_word_button = isset($current_options['show_word_upload_button']) ? $current_options['show_word_upload_button'] : 'on';
363 if ($show_word_button === 'on') {
364 echo ' <input type="file" id="word-upload-' . esc_attr($bot_id) . '" class="word-upload" accept=".docx" style="display: none;">';
365 echo ' <button id="word-upload-btn-' . esc_attr($bot_id) . '" class="toolbar-btn word-upload-btn" title="' . esc_attr__('Upload Word Document', 'mxchat') . '">';
366 echo ' <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" stroke="currentColor">';
367 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>';
368 echo ' </button>';
369 }
370
371 // File containers
372 echo ' <div id="active-pdf-container-' . esc_attr($bot_id) . '" class="active-pdf-container" style="display: none;">';
373 echo ' <span id="active-pdf-name-' . esc_attr($bot_id) . '" class="active-pdf-name"></span>';
374 echo ' <button id="remove-pdf-btn-' . esc_attr($bot_id) . '" class="remove-pdf-btn" title="' . esc_attr__('Remove PDF', 'mxchat') . '">';
375 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">';
376 echo ' <line x1="18" y1="6" x2="6" y2="18"></line>';
377 echo ' <line x1="6" y1="6" x2="18" y2="18"></line>';
378 echo ' </svg>';
379 echo ' </button>';
380 echo ' </div>';
381
382 echo ' <div id="active-word-container-' . esc_attr($bot_id) . '" class="active-word-container" style="display: none;">';
383 echo ' <span id="active-word-name-' . esc_attr($bot_id) . '" class="active-word-name"></span>';
384 echo ' <button id="remove-word-btn-' . esc_attr($bot_id) . '" class="remove-word-btn" title="' . esc_attr__('Remove Word Document', 'mxchat') . '">';
385 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">';
386 echo ' <line x1="18" y1="6" x2="6" y2="18"></line>';
387 echo ' <line x1="6" y1="6" x2="18" y2="18"></line>';
388 echo ' </svg>';
389 echo ' </button>';
390 echo ' </div>';
391
392 // Perplexity Button
393 if (apply_filters('mxchat_perplexity_should_show_logo', true)) {
394 echo ' <button id="perplexity-search-btn-' . esc_attr($bot_id) . '" class="toolbar-btn perplexity-search-btn" title="' . esc_attr__('Search with Perplexity', 'mxchat-perplexity') . '">';
395 echo ' <svg fill="currentColor" height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">';
396 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>';
397 echo ' </svg>';
398 echo ' </button>';
399 }
400
401
402 // Image Analysis Button - hidden by default, controlled by MxChat Vision add-on
403 echo ' <input type="file" id="image-upload-' . esc_attr($bot_id) . '" class="image-upload" accept="image/*" style="display: none;" multiple>';
404 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;">';
405 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;">';
406 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;"/>';
407 echo ' <circle cx="12" cy="13" r="3" style="fill: none !important;"/>';
408 echo ' </svg>';
409 echo ' </button>';
410
411
412 echo ' </div>';
413
414 echo ' <div class="chatbot-footer">';
415
416 // Output the privacy notice if enabled
417 if ($privacy_toggle && !empty($privacy_text)) {
418 echo '<p class="privacy-notice">' . $privacy_text . '</p>';
419 }
420
421 echo ' </div>';
422 echo ' </div>';
423 echo ' </div>';
424 echo '</div>';
425
426 if ($is_floating) {
427 echo '</div>';
428
429 if (!empty($pre_chat_message)) {
430 // Rendered hidden by default — JS checkPreChatDismissal() handles show/hide via localStorage
431 echo '<div id="pre-chat-message-' . esc_attr($bot_id) . '" class="pre-chat-message" style="display:none;">';
432 echo nl2br(esc_html($pre_chat_message));
433 echo '<button class="close-pre-chat-message" aria-label="' . esc_attr__('Close', 'mxchat') . '">&times;</button>';
434 echo '</div>';
435 }
436
437 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) . ';"') . '>';
438 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>';
439
440 if (!empty($custom_icon)) {
441 echo '<img src="' . $custom_icon . '" alt="' . esc_attr__('Chatbot Icon', 'mxchat') . '" style="height: 48px; width: 48px; object-fit: contain;" />';
442 } else {
443 $widget_icon_style = 'height: 48px; width: 48px;';
444 if (!$skip_inline_colors) {
445 $widget_icon_style .= ' fill: ' . esc_attr($icon_color) . ';';
446 }
447 echo '<svg id="widget_icon_10" style="' . $widget_icon_style . '" viewBox="0 0 1120 1120" fill="none" xmlns="http://www.w3.org/2000/svg">';
448 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>';
449 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>';
450 echo '</svg>';
451 }
452 echo '</div>';
453 }
454
455 return ob_get_clean();
456 }
457
458 /**
459 * Get bot-specific options for multi-bot functionality
460 * Falls back to default options if bot_id is 'default' or multi-bot add-on is not active
461 */
462 private function get_bot_options($bot_id = 'default') {
463 // If default bot or multi-bot add-on not active, return empty (use default options)
464 if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
465 return array();
466 }
467
468 //Hook for multi-bot add-on to provide bot-specific options
469 $bot_options = apply_filters('mxchat_get_bot_options', array(), $bot_id);
470
471 return is_array($bot_options) ? $bot_options : array();
472 }
473
474 /**
475 * Get bot-specific Pinecone configuration
476 * Used in the knowledge retrieval functions
477 */
478 private function get_bot_pinecone_config($bot_id = 'default') {
479 // If default bot or multi-bot add-on not active, use default Pinecone config
480 if ($bot_id === 'default' || !class_exists('MxChat_Multi_Bot_Manager')) {
481 $addon_options = get_option('mxchat_pinecone_addon_options', array());
482 return array(
483 'use_pinecone' => (isset($addon_options['mxchat_use_pinecone']) && $addon_options['mxchat_use_pinecone'] === '1'),
484 'api_key' => $addon_options['mxchat_pinecone_api_key'] ?? '',
485 'host' => $addon_options['mxchat_pinecone_host'] ?? '',
486 'namespace' => $addon_options['mxchat_pinecone_namespace'] ?? ''
487 );
488 }
489
490 // Hook for multi-bot add-on to provide bot-specific Pinecone config
491 $bot_pinecone_config = apply_filters('mxchat_get_bot_pinecone_config', array(), $bot_id);
492
493 return is_array($bot_pinecone_config) ? $bot_pinecone_config : array();
494 }
495
496
497 private function determine_email_collection_state() {
498 // Logged-in users skip the email form
499 if (is_user_logged_in()) {
500 $current_user = wp_get_current_user();
501 return [
502 'show_email_form' => false,
503 'user_email' => $current_user->user_email,
504 'user_name' => $current_user->display_name ?: $current_user->first_name ?: ''
505 ];
506 }
507
508 // For guests, default to showing the email form.
509 // The session-based check happens client-side via AJAX since the
510 // session ID lives in the browser cookie/localStorage.
511 return [
512 'show_email_form' => true,
513 'user_email' => '',
514 'user_name' => ''
515 ];
516 }
517
518
519 /**
520 * NEW: Determine if and which chatbot should be displayed
521 */
522 private function get_display_bot() {
523 // Get page-specific settings from meta box
524 $page_setting = $this->get_page_bot_setting();
525
526 // Get global settings - FIXED: Check for 'on' instead of 'on'
527 $global_autoshow = isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on';
528 $global_default_bot = isset($this->options['default_bot']) ? $this->options['default_bot'] : 'default';
529
530 // If page specifically hides chatbot, don't show anything
531 if ($page_setting && $page_setting['action'] === 'hide') {
532 return false;
533 }
534
535 // If page specifies a specific bot, use that
536 if ($page_setting && $page_setting['action'] === 'show') {
537 return $page_setting['bot_id'];
538 }
539
540 // Page setting is 'global' or no page setting exists
541 // Check global auto-show setting
542 if ($global_autoshow) {
543 // Check post type visibility settings
544 if (!$this->should_show_on_current_post_type()) {
545 return false;
546 }
547
548 // Global auto-show is enabled, return the default bot
549 return $global_default_bot;
550 }
551
552 // Global auto-show is disabled and no page-specific bot selected
553 // Don't show chatbot (user should use shortcodes)
554 return false;
555 }
556
557 /**
558 * Check if chatbot should be shown on the current post type
559 */
560 private function should_show_on_current_post_type() {
561 // Get visibility settings
562 $mode = isset($this->options['post_type_visibility_mode']) ? $this->options['post_type_visibility_mode'] : 'all';
563 $list = isset($this->options['post_type_visibility_list']) ? $this->options['post_type_visibility_list'] : array();
564
565 // Ensure list is an array
566 if (!is_array($list)) {
567 $list = array();
568 }
569
570 // If mode is 'all', show on all post types
571 if ($mode === 'all') {
572 return true;
573 }
574
575 // Get current post type
576 $current_post_type = $this->get_current_post_type();
577
578 // If we can't determine post type, default to showing
579 if (empty($current_post_type)) {
580 return true;
581 }
582
583 // Check based on mode
584 if ($mode === 'include') {
585 // Only show on selected post types
586 return in_array($current_post_type, $list);
587 } elseif ($mode === 'exclude') {
588 // Hide on selected post types
589 return !in_array($current_post_type, $list);
590 }
591
592 // Default to showing
593 return true;
594 }
595
596 /**
597 * Get the current post type
598 */
599 private function get_current_post_type() {
600 // Try to get from queried object first
601 $queried_object = get_queried_object();
602
603 if ($queried_object instanceof WP_Post) {
604 return $queried_object->post_type;
605 }
606
607 // Try get_post_type()
608 $post_type = get_post_type();
609 if ($post_type) {
610 return $post_type;
611 }
612
613 // Check if we're on an archive
614 if (is_post_type_archive()) {
615 return get_query_var('post_type');
616 }
617
618 // Check common archive types
619 if (is_home() || is_single()) {
620 return 'post';
621 }
622
623 if (is_page()) {
624 return 'page';
625 }
626
627 return '';
628 }
629
630 /**
631 * Get page-specific bot setting using new visibility field with backward compat
632 */
633 private function get_page_bot_setting($post_id = null) {
634 if (!$post_id) {
635 $post_id = get_the_ID();
636 }
637
638 if (!$post_id) {
639 return null;
640 }
641
642 // Check new visibility field first
643 $visibility = get_post_meta($post_id, '_mxchat_page_visibility', true);
644
645 if ($visibility === 'hide') {
646 return array('action' => 'hide');
647 }
648
649 if ($visibility === 'show') {
650 $selected_bot = get_post_meta($post_id, '_mxchat_selected_bot', true);
651 $bot_id = !empty($selected_bot) ? $selected_bot : 'default';
652 return array('action' => 'show', 'bot_id' => $bot_id);
653 }
654
655 // Backward compat: check legacy hide checkbox if no new field set
656 if (empty($visibility)) {
657 $hide_chatbot = get_post_meta($post_id, '_mxchat_hide_chatbot', true);
658 if ($hide_chatbot === '1') {
659 return array('action' => 'hide');
660 }
661 }
662
663 // Check if specific bot is selected (legacy path)
664 $selected_bot = get_post_meta($post_id, '_mxchat_selected_bot', true);
665 if (!empty($selected_bot)) {
666 return array('action' => 'show', 'bot_id' => $selected_bot);
667 }
668
669 // Use global setting
670 return array('action' => 'global');
671 }
672
673 /**
674 * NEW: Determine which bot to use for shortcode
675 */
676 private function determine_bot_for_shortcode($shortcode_bot_id) {
677 // If bot_id explicitly provided in shortcode, use that
678 if (!empty($shortcode_bot_id)) {
679 return $shortcode_bot_id;
680 }
681
682 // No bot_id in shortcode, check page setting
683 $page_setting = $this->get_page_bot_setting();
684 if ($page_setting && $page_setting['action'] === 'show') {
685 return $page_setting['bot_id'];
686 }
687
688 // Fall back to default
689 return 'default';
690 }
691
692 /**
693 * NEW: Helper to check if auto-append is enabled globally
694 */
695 private function is_auto_append_enabled() {
696 return isset($this->options['append_to_body']) && $this->options['append_to_body'] === 'on';
697 }
698
699 /**
700 * UPDATED: Debug function with new context info
701 */
702
703 /**
704 * NEW: Helper method to get available bots (for admin notices, etc.)
705 */
706 private function get_available_bots() {
707 $bots = array('default' => __('Default Bot', 'mxchat'));
708
709 // Check if multi-bot addon is active
710 if (class_exists('MxChat_Multi_Bot_Manager')) {
711 $multi_bot_manager = MxChat_Multi_Bot_Core_Manager::get_instance();
712 $available_bots = $multi_bot_manager->get_available_bots();
713
714 // Add the available bots
715 foreach ($available_bots as $bot_id => $bot_name) {
716 $bots[$bot_id] = $bot_name;
717 }
718 }
719
720 return $bots;
721 }
722
723
724
725 }
726 ?>