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

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