403]); } check_admin_referer('chatnex_admin_language'); $language = isset($_POST['chatnex_admin_language']) ? sanitize_text_field(wp_unslash($_POST['chatnex_admin_language'])) : ''; if (!in_array($language, ['lt', 'en'], true)) { wp_die(esc_html__('Invalid language.', 'chatnex'), '', ['response' => 400]); } $settings = get_option('chatnex_settings', []); $settings = is_array($settings) ? $settings : []; $settings['admin_language'] = $language; update_option('chatnex_settings', $settings, false); wp_safe_redirect(admin_url('admin.php?page=chatnex')); exit; } public function handle_service_connection() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You are not allowed to manage ChatNEX.', 'chatnex'), '', ['response' => 403]); } check_admin_referer('chatnex_service_connection'); $connect = isset($_POST['chatnex_connect']) && '1' === sanitize_text_field(wp_unslash($_POST['chatnex_connect'])); update_option('chatnex_service_consent', $connect ? time() : false, false); if ($connect) { self::mark_auto_register_pending(); $this->maybe_auto_register_project('administrator_opt_in'); } $this->purge_public_page_cache(); wp_safe_redirect(admin_url('admin.php?page=chatnex')); exit; } public function add_privacy_policy_content() { if (!function_exists('wp_add_privacy_policy_content')) { return; } wp_add_privacy_policy_content('ChatNEX', wp_kses_post( '
' . __('This site uses the ChatNEX AI assistant provided by ENEX MB. After the site administrator connects the service, the site URL, name, plugin version and assistant settings are sent to https://chat.enex.lt. When a visitor uses the chat, their messages, conversation identifier, language, page URL and any contact details they choose to enter are sent to ChatNEX to answer questions and deliver enquiries. ChatNEX uses OpenAI to process AI requests. The chat stores conversation state in browser storage and periodically sends service availability requests while the widget is active.', 'chatnex') . '
' . '' . __('Site owners must explain their own purposes, legal basis, retention and contact handling in their privacy notice. Do not enter passwords, payment card details or sensitive personal information in the chat. Contact info@enex.lt for information about the ChatNEX service and data requests. Stripe processes subscription payments outside this website.', 'chatnex') . '
' )); } public function register_admin_menu() { add_menu_page( 'ChatNEX', 'ChatNEX', 'manage_options', 'chatnex', [$this, 'render_settings_page'], 'dashicons-format-chat', 56 ); add_submenu_page( 'chatnex', chatnex_admin_text('Išvaizda', 'Appearance'), chatnex_admin_text('Išvaizda', 'Appearance'), 'manage_options', 'chatnex-design', [$this, 'render_design_page'] ); add_submenu_page( 'chatnex', chatnex_admin_text('DI elgsena', 'AI behavior'), chatnex_admin_text('DI elgsena', 'AI behavior'), 'manage_options', 'chatnex-ai-behavior', [$this, 'render_ai_behavior_page'] ); } public static function mark_auto_register_pending() { if (defined('WP_INSTALLING') && WP_INSTALLING) { return; } if (!chatnex_service_connected()) { return; } update_option('chatnex_auto_register_pending', time(), false); delete_transient('chatnex_auto_register_lock'); } public function maybe_auto_register_project_in_admin() { if (!is_admin()) { return; } if (!current_user_can('manage_options')) { return; } if (defined('WP_INSTALLING') && WP_INSTALLING) { return; } if (wp_doing_ajax()) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin-page routing, never authorizes a settings change or service consent. $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; $is_chatnex_page = strpos($page, 'chatnex') === 0; $pending = (int) get_option('chatnex_auto_register_pending', 0); if (!$pending && !$is_chatnex_page) { return; } $this->maybe_auto_register_project($is_chatnex_page ? 'chatnex_admin' : 'activation_pending'); } public function maybe_auto_register_project($source = 'manual') { if (!chatnex_service_connected()) { return false; } if (defined('WP_INSTALLING') && WP_INSTALLING) { return false; } if (wp_doing_ajax()) { return false; } $lock_key = 'chatnex_auto_register_lock'; if (get_transient($lock_key)) { return false; } $settings = get_option($this->option_name, []); $settings = is_array($settings) ? $settings : []; $backend_url = 'https://chat.enex.lt'; $project_id = isset($settings['project_id']) ? strtolower(sanitize_text_field((string) $settings['project_id'])) : ''; $public_key = isset($settings['public_key']) ? sanitize_text_field((string) $settings['public_key']) : ''; $site_url = home_url('/'); $site_name = get_bloginfo('name'); if (empty($site_url) || empty($backend_url)) { return false; } set_transient($lock_key, 1, 120); $body = [ 'site_url' => $site_url, 'site_name' => $site_name, 'source' => $source, 'plugin_version' => CHATNEX_VERSION, ]; if (!empty($project_id)) { $body['project_id'] = $project_id; } if (!empty($public_key)) { $body['public_key'] = $public_key; } $request_args = [ 'timeout' => 3, 'redirection' => 2, 'blocking' => true, 'sslverify' => true, 'headers' => [ 'Content-Type' => 'application/json', ], 'body' => wp_json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]; $response = wp_remote_post($backend_url . '/api/project-auto-register', $request_args); delete_transient($lock_key); if (is_wp_error($response)) { update_option('chatnex_auto_register_pending', time(), false); update_option('chatnex_auto_register_last_status', 'warning', false); update_option('chatnex_auto_register_last_message', $response->get_error_message(), false); return false; } $code = wp_remote_retrieve_response_code($response); $raw_body = wp_remote_retrieve_body($response); if ($code < 200 || $code >= 300 || empty($raw_body)) { update_option('chatnex_auto_register_pending', time(), false); update_option('chatnex_auto_register_last_status', 'warning', false); update_option('chatnex_auto_register_last_message', 'Backend grąžino netinkamą atsakymą: HTTP ' . (int) $code, false); return false; } $data = json_decode($raw_body, true); if (!is_array($data) || empty($data['ok'])) { update_option('chatnex_auto_register_pending', time(), false); update_option('chatnex_auto_register_last_status', 'warning', false); update_option('chatnex_auto_register_last_message', 'Backend atsakymas neatpažintas.', false); return false; } $resolved_project_id = isset($data['project_id']) ? strtolower(sanitize_text_field((string) $data['project_id'])) : ''; $resolved_public_key = isset($data['public_key']) ? sanitize_text_field((string) $data['public_key']) : ''; $billing_management_token = isset($data['billing_management_token']) ? sanitize_text_field((string) $data['billing_management_token']) : ''; if (empty($resolved_project_id) && empty($resolved_public_key)) { update_option('chatnex_auto_register_pending', time(), false); update_option('chatnex_auto_register_last_status', 'warning', false); update_option('chatnex_auto_register_last_message', 'Backend negrąžino project_id arba public_key.', false); return false; } $updated = $settings; $updated['backend_url'] = $backend_url; if (!empty($resolved_project_id)) { $updated['project_id'] = $resolved_project_id; } if (!empty($resolved_public_key)) { $updated['public_key'] = $resolved_public_key; } update_option($this->option_name, $updated, false); if (!empty($billing_management_token)) { update_option('chatnex_billing_management_token', $billing_management_token, false); } delete_option('chatnex_auto_register_pending'); delete_option('chatnex_auto_register_last_status'); delete_option('chatnex_auto_register_last_message'); $this->sync_ai_behavior_settings_to_backend($updated); $this->sync_widget_settings_to_backend($updated); $this->purge_public_page_cache(); update_option('chatnex_last_live_sync_version', CHATNEX_VERSION, false); return true; } public function register_settings() { register_setting('chatnex_settings_group', $this->option_name, [$this, 'sanitize_settings']); add_settings_section( 'chatnex_main_section', 'Pagrindiniai nustatymai', function () { echo '' . esc_html(chatnex_admin_text('Nustatykite ChatNEX įskiepį ir prijunkite prie centrinio backend.', 'Configure the ChatNEX plugin and connect it to the central backend.')) . '
'; }, 'chatnex' ); add_settings_field( 'enabled', chatnex_admin_text('Įjungti chat', 'Enable chat'), [$this, 'render_enabled_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'widget_language', chatnex_admin_text('Chat kalba', 'Visitor chat language'), [$this, 'render_widget_language_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'admin_language', chatnex_admin_text('Įskiepio administravimo kalba', 'Plugin administration language'), [$this, 'render_admin_language_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'project_id', chatnex_admin_text('Projekto ID', 'Project ID'), [$this, 'render_project_id_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'public_key', chatnex_admin_text('Public key', 'Public key'), [$this, 'render_public_key_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'lead_recipients', chatnex_admin_text('Užklausų gavėjai', 'Lead recipients'), [$this, 'render_lead_recipients_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'chat_title', chatnex_admin_text('Chat antraštė', 'Chat title'), [$this, 'render_chat_title_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'helper_text', chatnex_admin_text('Helper tekstas', 'Helper text'), [$this, 'render_helper_text_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'welcome_text', chatnex_admin_text('Pradinis tekstas', 'Welcome message'), [$this, 'render_welcome_text_field'], 'chatnex', 'chatnex_main_section' ); add_settings_field( 'quick_questions', chatnex_admin_text('Greiti klausimai', 'Quick questions'), [$this, 'render_quick_questions_field'], 'chatnex', 'chatnex_main_section' ); add_settings_section( 'chatnex_ai_behavior_section', 'DI elgsena', function () { echo '' . esc_html(chatnex_admin_text('Nustatykite, kaip ChatNEX bendrauja su lankytoju: emoji, toną, šiltumą ir kontaktų siūlymą.', 'Configure how ChatNEX communicates with visitors: emoji, tone, warmth and contact behavior.')) . '
'; }, 'chatnex-ai-behavior' ); add_settings_field( 'additional_ai_context', chatnex_admin_text('Papildomos AI taisyklės / informacija', 'Additional AI rules / information'), [$this, 'render_additional_ai_context_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_emoji_enabled', chatnex_admin_text('Naudoti emoji', 'Use emoji'), [$this, 'render_ai_emoji_enabled_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_emoji_level', chatnex_admin_text('Emoji kiekis (0–100)', 'Emoji amount (0–100)'), [$this, 'render_ai_emoji_level_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_tone_style', chatnex_admin_text('Bendravimo tonas', 'Communication tone'), [$this, 'render_ai_tone_style_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_warmth_level', chatnex_admin_text('Bendravimo šiltumas (0–100)', 'Communication warmth (0–100)'), [$this, 'render_ai_warmth_level_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_contact_stage_action', chatnex_admin_text('Kontaktų etapo veiksmas', 'Contact-stage action'), [$this, 'render_ai_contact_stage_action_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_contact_trigger_after_ai_replies', chatnex_admin_text('Priverstinai prašyti / duoti kontaktus po AI atsakymų', 'Force a contact action after AI replies'), [$this, 'render_ai_contact_trigger_after_ai_replies_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_field( 'ai_uncertainty_level', chatnex_admin_text('Atsakymo atsargumas (0–100)', 'Answer caution (0–100)'), [$this, 'render_ai_uncertainty_level_field'], 'chatnex-ai-behavior', 'chatnex_ai_behavior_section' ); add_settings_section( 'chatnex_design_section', chatnex_admin_text('Išvaizdos nustatymai', 'Appearance settings'), function () { echo '' . esc_html(chatnex_admin_text('Čia galite reguliuoti ChatNEX spalvas, skaidrumą ir vizualinius elementus.', 'Configure ChatNEX colors, transparency and visual elements.')) . '
'; }, 'chatnex-design' ); add_settings_field( 'bubble_color', chatnex_admin_text('Pranešimo burbuliuko spalva', 'Notification bubble color'), [$this, 'render_bubble_color_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'bubble_opacity', chatnex_admin_text('Pranešimo burbuliuko skaidrumas', 'Notification bubble transparency'), [$this, 'render_bubble_opacity_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'bubble_size', chatnex_admin_text('Chat burbuliuko dydis', 'Chat bubble size'), [$this, 'render_bubble_size_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'bubble_icon', chatnex_admin_text('Pranešimo burbuliuko ikonėlė', 'Notification bubble icon'), [$this, 'render_bubble_icon_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'header_color', chatnex_admin_text('Chat lango viršutinės dalies spalva', 'Chat header color'), [$this, 'render_header_color_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'header_opacity', chatnex_admin_text('Chat lango viršutinės dalies skaidrumas', 'Chat header transparency'), [$this, 'render_header_opacity_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'send_button_color', chatnex_admin_text('Siųsti mygtuko spalva', 'Send button color'), [$this, 'render_send_button_color_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'chat_background_color', chatnex_admin_text('Chat lango fono spalva', 'Chat window background color'), [$this, 'render_chat_background_color_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'chat_background_opacity', chatnex_admin_text('Chat lango skaidrumas', 'Chat window transparency'), [$this, 'render_chat_background_opacity_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'logo_url', chatnex_admin_text('Logotipo demonstravimas viršutinėje chat dalyje', 'Logo shown in the chat header'), [$this, 'render_logo_url_field'], 'chatnex-design', 'chatnex_design_section' ); add_settings_field( 'avatar_preset', chatnex_admin_text('Asistento ženkliukas', 'Assistant avatar'), [$this, 'render_avatar_preset_field'], 'chatnex-design', 'chatnex_design_section' ); } private function sync_ai_behavior_settings_to_backend($settings) { if (!chatnex_service_connected()) { return; } $project_id = isset($settings['project_id']) ? strtolower(sanitize_text_field((string) $settings['project_id'])) : ''; $backend_url = isset($settings['backend_url']) ? untrailingslashit(esc_url_raw((string) $settings['backend_url'])) : ''; $public_key = isset($settings['public_key']) ? sanitize_text_field((string) $settings['public_key']) : ''; if (empty($project_id) || empty($backend_url)) { return; } $payload = [ 'project_id' => $project_id, 'public_key' => $public_key, 'style' => [ 'emoji_enabled' => !empty($settings['ai_emoji_enabled']), 'emoji_level' => intval($settings['ai_emoji_level'] ?? 50), 'tone_style' => (string) ($settings['ai_tone_style'] ?? 'friendly'), 'warmth_level' => intval($settings['ai_warmth_level'] ?? 70), 'contact_stage_action' => (string) ($settings['ai_contact_stage_action'] ?? 'no_contact_flow'), 'contact_strategy_balance' => intval($settings['ai_contact_strategy_balance'] ?? $settings['ai_cta_level'] ?? 50), 'uncertainty_level' => intval($settings['ai_uncertainty_level'] ?? 80), 'contact_offer_mode' => 'never', 'registration_offer_mode' => 'never', 'contact_trigger_after_ai_replies' => (int) ($settings['ai_contact_trigger_after_ai_replies'] ?? 0), ], ]; wp_remote_post($backend_url . '/api/project-ai-behavior/sync', [ 'timeout' => 8, 'redirection' => 3, 'sslverify' => true, 'headers' => [ 'Content-Type' => 'application/json', ], 'body' => wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]); } private function sync_widget_settings_to_backend($settings) { if (!chatnex_service_connected()) { return; } $project_id = isset($settings['project_id']) ? strtolower(sanitize_text_field((string) $settings['project_id'])) : ''; $backend_url = isset($settings['backend_url']) ? untrailingslashit(esc_url_raw((string) $settings['backend_url'])) : ''; $public_key = isset($settings['public_key']) ? sanitize_text_field((string) $settings['public_key']) : ''; if (empty($project_id) || empty($backend_url)) { return; } $chat_background_opacity = intval($settings['chat_background_opacity'] ?? 0); $header_opacity = intval($settings['header_opacity'] ?? 0); $bubble_opacity = intval($settings['bubble_opacity'] ?? 0); $max_transparency = max($chat_background_opacity, $header_opacity, $bubble_opacity); $payload = [ 'project_id' => $project_id, 'public_key' => $public_key, 'site_url' => home_url('/'), 'plugin_version' => CHATNEX_VERSION, 'additional_ai_context' => (string) ($settings['additional_ai_context'] ?? ''), 'widget' => [ 'widget_enabled' => !empty($settings['enabled']), 'widget_position' => 'bottom-left', 'widget_size' => 'medium', 'widget_theme' => 'custom', 'primary_color' => (string) ($settings['bubble_color'] ?? ''), 'secondary_color' => (string) ($settings['header_color'] ?? ''), 'accent_color' => (string) ($settings['send_button_color'] ?? ''), 'transparency_enabled' => $max_transparency > 0, 'transparency_value' => max(0, min(100, 100 - $max_transparency)), 'bubble_label' => (string) ($settings['helper_text'] ?? ''), 'welcome_message' => (string) ($settings['welcome_text'] ?? ''), 'input_placeholder' => (($settings['widget_language'] ?? 'lt') === 'en') ? 'Type your message...' : 'Parašykite žinutę...', 'send_button_label' => (($settings['widget_language'] ?? 'lt') === 'en') ? 'Send' : 'Siųsti', 'header_title' => (string) ($settings['chat_title'] ?? ''), 'header_subtitle' => (string) ($settings['helper_text'] ?? ''), 'show_branding' => false, ], ]; wp_remote_post($backend_url . '/api/project-widget-settings/sync', [ 'timeout' => 8, 'redirection' => 3, 'sslverify' => true, 'headers' => [ 'Content-Type' => 'application/json', ], 'body' => wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]); } public function maybe_sync_live_settings_after_update() { if (!chatnex_service_connected()) { return; } if (defined('WP_INSTALLING') && WP_INSTALLING) { return; } if (function_exists('wp_doing_ajax') && wp_doing_ajax()) { return; } $last_synced_version = (string) get_option('chatnex_last_live_sync_version', ''); if ($last_synced_version === CHATNEX_VERSION) { return; } $lock_key = 'chatnex_live_settings_version_sync_lock'; if (get_transient($lock_key)) { return; } set_transient($lock_key, 1, 120); $settings = $this->get_settings(); $project_id = isset($settings['project_id']) ? trim((string) $settings['project_id']) : ''; $backend_url = isset($settings['backend_url']) ? trim((string) $settings['backend_url']) : ''; if ($project_id === '' || $backend_url === '') { delete_transient($lock_key); return; } $this->sync_ai_behavior_settings_to_backend($settings); $this->sync_widget_settings_to_backend($settings); $this->purge_public_page_cache(); update_option('chatnex_last_live_sync_version', CHATNEX_VERSION, false); delete_transient($lock_key); } private function purge_public_page_cache() { if (function_exists('wp_cache_clear_cache')) { wp_cache_clear_cache(); } if (function_exists('wp_cache_clean_cache')) { global $file_prefix; wp_cache_clean_cache($file_prefix ?? ''); } if (function_exists('wpsc_delete_post_cache')) { $front_page_id = (int) get_option('page_on_front'); if ($front_page_id > 0) { wpsc_delete_post_cache($front_page_id); } } if (function_exists('rocket_clean_domain')) { rocket_clean_domain(); } if (has_action('litespeed_purge_all')) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Official LiteSpeed Cache integration hook. do_action('litespeed_purge_all'); } if (function_exists('wp_cache_flush')) { wp_cache_flush(); } } private function get_central_project_meta($project_id, $backend_url, $public_key = '') { if (!chatnex_service_connected()) { return null; } $project_id = strtolower(sanitize_text_field((string) $project_id)); $backend_url = untrailingslashit(esc_url_raw((string) $backend_url)); $public_key = sanitize_text_field((string) $public_key); if (empty($project_id) || empty($backend_url)) { return null; } $url = add_query_arg([ 'project_id' => $project_id, 'public_key' => $public_key, ], $backend_url . '/api/project-public-meta'); $response = wp_remote_get($url, [ 'timeout' => 8, 'redirection' => 3, 'sslverify' => true, ]); if (is_wp_error($response)) { return null; } $code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); if ($code < 200 || $code >= 300 || empty($body)) { return null; } $data = json_decode($body, true); if (!is_array($data)) { return null; } if (isset($data['project_meta']) && is_array($data['project_meta'])) { return $data['project_meta']; } return $data; } public function handle_autosave_settings() { if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Neturite teisės keisti ChatNEX nustatymų.'], 403); } check_ajax_referer('chatnex_autosave_settings', 'nonce'); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Each settings field is validated and sanitized by sanitize_settings() below. $input = isset($_POST[$this->option_name]) && is_array($_POST[$this->option_name]) ? wp_unslash($_POST[$this->option_name]) : []; if (!is_array($input)) { wp_send_json_error(['message' => 'Neteisingi nustatymų duomenys.'], 400); } $sanitized = $this->sanitize_settings($input); update_option($this->option_name, $sanitized, false); wp_send_json_success([ 'message' => 'Išsaugota', 'saved_at' => current_time('mysql'), 'version' => CHATNEX_VERSION, ]); } private function proxy_billing_request($endpoint, $payload = [], $receipt_response = false) { if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Neturite teisės valdyti ChatNEX prenumeratos.'], 403); } check_ajax_referer('chatnex_billing_management', 'nonce'); if (!chatnex_service_connected()) { wp_send_json_error(['message' => __('Connect ChatNEX before managing a subscription.', 'chatnex')], 409); } $settings = $this->get_settings(); $project_id = isset($settings['project_id']) ? strtolower(sanitize_text_field((string) $settings['project_id'])) : ''; $billing_token = sanitize_text_field((string) get_option('chatnex_billing_management_token', '')); if (empty($project_id) || empty($billing_token)) { wp_send_json_error(['message' => 'Trūksta projekto mokėjimų valdymo rakto.'], 409); } $response = wp_remote_post('https://chat.enex.lt' . $endpoint, [ 'timeout' => 20, 'redirection' => 0, 'blocking' => true, 'sslverify' => true, 'headers' => [ 'Content-Type' => 'application/json', ], 'body' => wp_json_encode(array_merge([ 'project_id' => $project_id, 'billing_management_token' => $billing_token, ], $payload), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ]); if (is_wp_error($response)) { wp_send_json_error(['message' => $response->get_error_message()], 502); } $code = (int) wp_remote_retrieve_response_code($response); $data = json_decode(wp_remote_retrieve_body($response), true); $data = is_array($data) ? $data : []; if ($receipt_response && $code >= 200 && $code < 300 && !empty($data['receipt_id']) && preg_match('/^[a-f0-9-]{36}$/', (string) $data['receipt_id'])) { wp_send_json_success([ 'receipt_id' => sanitize_text_field($data['receipt_id']), 'received_at' => sanitize_text_field($data['received_at'] ?? ''), 'receipt_text' => sanitize_textarea_field($data['receipt_text'] ?? ''), 'email_accepted' => !empty($data['email_accepted']), 'refund_completed' => false, ]); } if ($code < 200 || $code >= 300 || empty($data['url'])) { $message = isset($data['error']) ? sanitize_text_field((string) $data['error']) : 'ChatNEX mokėjimų serveris grąžino klaidą.'; wp_send_json_error(['message' => $message], $code >= 400 && $code <= 599 ? $code : 502); } $url = esc_url_raw((string) $data['url']); $host = strtolower((string) wp_parse_url($url, PHP_URL_HOST)); if (!in_array($host, ['checkout.stripe.com', 'billing.stripe.com'], true)) { wp_send_json_error(['message' => 'Gauta neleistina mokėjimo nuoroda.'], 502); } wp_send_json_success(['url' => $url]); } public function handle_billing_checkout() { check_ajax_referer('chatnex_billing_management', 'nonce'); $interval = isset($_POST['interval']) ? sanitize_key((string) wp_unslash($_POST['interval'])) : ''; if (!in_array($interval, ['month', 'year'], true)) { wp_send_json_error(['message' => 'Neteisingas prenumeratos laikotarpis.'], 400); } $accepted = isset($_POST['terms_accepted']) && '1' === sanitize_text_field(wp_unslash($_POST['terms_accepted'])); if (!$accepted) { wp_send_json_error(['message' => chatnex_admin_text('Patvirtinkite prenumeratos sąlygas.', 'Please accept the subscription terms.')], 400); } $this->proxy_billing_request('/api/billing/checkout', ['interval' => $interval, 'terms_accepted' => true, 'legal_version' => CHATNEX_LEGAL_VERSION]); } public function handle_billing_withdrawal() { check_ajax_referer('chatnex_billing_management', 'nonce'); $name = isset($_POST['customer_name']) ? sanitize_text_field(wp_unslash($_POST['customer_name'])) : ''; $confirmed = isset($_POST['confirm_withdrawal']) && '1' === sanitize_text_field(wp_unslash($_POST['confirm_withdrawal'])); if (!$confirmed || strlen($name) < 2 || strlen($name) > 180) { wp_send_json_error(['message' => chatnex_admin_text('Įrašykite vardą ir patvirtinkite pareiškimą.', 'Enter your name and confirm the statement.')], 400); } $this->proxy_billing_request('/api/billing/withdrawal', ['customer_name' => $name, 'confirm_withdrawal' => true], true); } public function handle_billing_portal() { $this->proxy_billing_request('/api/billing/portal'); } public function sanitize_settings($input) { $output = $this->get_settings(); $settings_scope = isset($input['settings_scope']) ? sanitize_key((string) $input['settings_scope']) : ''; if (array_key_exists('enabled', $input)) { $output['enabled'] = !empty($input['enabled']) ? 1 : 0; } $output['backend_url'] = 'https://chat.enex.lt'; $admin_language = isset($input['admin_language']) ? strtolower(sanitize_text_field((string) $input['admin_language'])) : ($output['admin_language'] ?? 'en'); $output['admin_language'] = in_array($admin_language, ['lt', 'en'], true) ? $admin_language : 'en'; $widget_language = isset($input['widget_language']) ? strtolower(sanitize_text_field((string) $input['widget_language'])) : ($output['widget_language'] ?? 'en'); $output['widget_language'] = in_array($widget_language, ['lt', 'en'], true) ? $widget_language : 'en'; $output['project_id'] = isset($input['project_id']) ? strtolower(sanitize_text_field($input['project_id'])) : ($output['project_id'] ?? ''); $output['public_key'] = isset($input['public_key']) ? sanitize_text_field($input['public_key']) : ($output['public_key'] ?? ''); $raw_recipients = isset($input['lead_recipients']) && is_array($input['lead_recipients']) ? $input['lead_recipients'] : ($output['lead_recipients'] ?? []); $clean_recipients = []; foreach ($raw_recipients as $recipient) { $recipient = sanitize_email($recipient); if (!$recipient || !is_email($recipient)) { continue; } $clean_recipients[] = strtolower($recipient); } $clean_recipients = array_values(array_unique($clean_recipients)); $output['lead_recipients'] = $clean_recipients; $output['chat_title'] = isset($input['chat_title']) ? sanitize_text_field($input['chat_title']) : ($output['chat_title'] ?? 'ChatNEX'); $output['helper_text'] = isset($input['helper_text']) ? sanitize_text_field($input['helper_text']) : ($output['helper_text'] ?? 'Mūsų Dirbtinis intelektas Jums padės 🙂'); $output['welcome_text'] = isset($input['welcome_text']) ? sanitize_textarea_field($input['welcome_text']) : ($output['welcome_text'] ?? 'Sveiki 👋 Kuo galiu padėti? Galite pasirinkti vieną iš greitų klausimų žemiau arba parašyti savo žinutę.'); $q1 = isset($input['quick_question_1']) ? sanitize_text_field($input['quick_question_1']) : ($output['quick_question_1'] ?? 'Kiek kainuoja?'); $q2 = isset($input['quick_question_2']) ? sanitize_text_field($input['quick_question_2']) : ($output['quick_question_2'] ?? 'Per kiek padarote?'); $q3 = isset($input['quick_question_3']) ? sanitize_text_field($input['quick_question_3']) : ($output['quick_question_3'] ?? ''); $output['quick_question_1'] = $q1; $output['quick_question_2'] = $q2; $output['quick_question_3'] = $q3; $output['quick_questions'] = implode(" ", array_values(array_filter([$q1, $q2, $q3]))); $output['additional_ai_context'] = isset($input['additional_ai_context']) ? sanitize_textarea_field($input['additional_ai_context']) : ($output['additional_ai_context'] ?? ''); $output['bubble_color'] = isset($input['bubble_color']) ? sanitize_hex_color($input['bubble_color']) : ($output['bubble_color'] ?? '#5b6df8'); $output['bubble_opacity'] = isset($input['bubble_opacity']) ? max(0, min(100, intval($input['bubble_opacity']))) : ($output['bubble_opacity'] ?? 0); $output['bubble_size'] = isset($input['bubble_size']) ? max(44, min(96, intval($input['bubble_size']))) : max(44, min(96, intval($output['bubble_size'] ?? 64))); $output['bubble_icon'] = isset($input['bubble_icon']) ? sanitize_text_field($input['bubble_icon']) : ($output['bubble_icon'] ?? 'chat'); $output['bubble_icon_custom_url'] = isset($input['bubble_icon_custom_url']) ? esc_url_raw(trim($input['bubble_icon_custom_url'])) : ($output['bubble_icon_custom_url'] ?? ''); $output['header_color'] = isset($input['header_color']) ? sanitize_hex_color($input['header_color']) : ($output['header_color'] ?? '#5b6df8'); $output['header_opacity'] = isset($input['header_opacity']) ? max(0, min(100, intval($input['header_opacity']))) : ($output['header_opacity'] ?? 0); $output['send_button_color'] = isset($input['send_button_color']) ? sanitize_hex_color($input['send_button_color']) : ($output['send_button_color'] ?? '#5b6df8'); $output['chat_background_color'] = isset($input['chat_background_color']) ? sanitize_hex_color($input['chat_background_color']) : ($output['chat_background_color'] ?? '#ffffff'); $output['chat_background_opacity'] = isset($input['chat_background_opacity']) ? max(0, min(100, intval($input['chat_background_opacity']))) : ($output['chat_background_opacity'] ?? 0); $output['logo_url'] = isset($input['logo_url']) ? esc_url_raw(trim($input['logo_url'])) : ($output['logo_url'] ?? ''); $output['avatar_preset'] = isset($input['avatar_preset']) ? sanitize_text_field($input['avatar_preset']) : ($output['avatar_preset'] ?? 'ai-robot'); $output['avatar_custom_url'] = isset($input['avatar_custom_url']) ? esc_url_raw(trim($input['avatar_custom_url'])) : ($output['avatar_custom_url'] ?? ''); $output['avatar_url'] = $this->resolve_avatar_url($output); $is_ai_behavior_save = isset($input['ai_emoji_level']) || isset($input['ai_tone_style']) || isset($input['ai_contact_stage_action']) || isset($input['ai_contact_trigger_after_ai_replies']); if ($is_ai_behavior_save) { $output['ai_emoji_enabled'] = !empty($input['ai_emoji_enabled']) ? 1 : 0; } else { $output['ai_emoji_enabled'] = isset($output['ai_emoji_enabled']) ? (int) $output['ai_emoji_enabled'] : 1; } $output['ai_emoji_level'] = isset($input['ai_emoji_level']) ? max(0, min(100, intval($input['ai_emoji_level']))) : ($output['ai_emoji_level'] ?? 50); $tone = isset($input['ai_tone_style']) ? sanitize_text_field($input['ai_tone_style']) : ($output['ai_tone_style'] ?? 'friendly'); $output['ai_tone_style'] = in_array($tone, ['neutral', 'friendly', 'warm', 'sales'], true) ? $tone : 'friendly'; $output['ai_warmth_level'] = isset($input['ai_warmth_level']) ? max(0, min(100, intval($input['ai_warmth_level']))) : ($output['ai_warmth_level'] ?? 70); $is_contact_settings_save = $settings_scope === 'ai_behavior' || array_key_exists('ai_contact_stage_request_customer_contact', $input) || array_key_exists('ai_contact_stage_show_company_contacts', $input) || array_key_exists('ai_contact_stage_action', $input); if ($is_contact_settings_save) { $requestCustomerContact = !empty($input['ai_contact_stage_request_customer_contact']); $showCompanyContacts = !empty($input['ai_contact_stage_show_company_contacts']); if ($requestCustomerContact && $showCompanyContacts) { $stageAction = 'mixed_contact_flow'; } elseif ($requestCustomerContact) { $stageAction = 'request_customer_contact'; } elseif ($showCompanyContacts) { $stageAction = 'show_company_contacts'; } else { $stageAction = 'no_contact_flow'; } if ( !isset($input['ai_contact_stage_request_customer_contact']) && !isset($input['ai_contact_stage_show_company_contacts']) && isset($input['ai_contact_stage_action']) ) { $legacyStageAction = sanitize_text_field($input['ai_contact_stage_action']); $allowedLegacyStageActions = [ 'no_contact_flow', 'request_customer_contact', 'show_company_contacts', 'mixed_contact_flow' ]; $stageAction = in_array($legacyStageAction, $allowedLegacyStageActions, true) ? $legacyStageAction : ($output['ai_contact_stage_action'] ?? 'no_contact_flow'); } $output['ai_contact_stage_action'] = $stageAction; } else { $output['ai_contact_stage_action'] = $output['ai_contact_stage_action'] ?? 'no_contact_flow'; } // Backward compatibility only. Real logic is controlled by ai_contact_stage_action. if ($output['ai_contact_stage_action'] === 'show_company_contacts') { $output['ai_contact_strategy_balance'] = 100; } elseif ($output['ai_contact_stage_action'] === 'request_customer_contact') { $output['ai_contact_strategy_balance'] = 0; } elseif ($output['ai_contact_stage_action'] === 'mixed_contact_flow') { $output['ai_contact_strategy_balance'] = 50; } else { $output['ai_contact_strategy_balance'] = 0; } $output['ai_cta_level'] = $output['ai_contact_strategy_balance']; $output['ai_uncertainty_level'] = isset($input['ai_uncertainty_level']) ? max(0, min(100, intval($input['ai_uncertainty_level']))) : ($output['ai_uncertainty_level'] ?? 80); // Legacy intensity fields are no longer user-facing. // Contact behavior is controlled only by ai_contact_stage_action + ai_contact_trigger_after_ai_replies. $output['ai_contact_offer_mode'] = 'never'; $output['ai_registration_offer_mode'] = 'never'; $output['ai_contact_trigger_after_ai_replies'] = isset($input['ai_contact_trigger_after_ai_replies']) ? max(0, min(10, intval($input['ai_contact_trigger_after_ai_replies']))) : ($output['ai_contact_trigger_after_ai_replies'] ?? 0); $this->sync_ai_behavior_settings_to_backend($output); $this->sync_widget_settings_to_backend($output); $this->purge_public_page_cache(); return $output; } private function get_settings() { $defaults = [ 'enabled' => 1, 'admin_language' => chatnex_get_admin_language(), 'widget_language' => 'en', 'backend_url' => 'https://chat.enex.lt', 'project_id' => '', 'public_key' => '', 'lead_recipients' => [], 'chat_title' => 'ChatNEX AI Assistant', 'helper_text' => 'Ask us anything 🙂', 'welcome_text' => 'Hi 👋 Ask me anything about our services, pricing, projects or contact details 🙂', 'quick_question_1' => 'What services do you offer?', 'quick_question_2' => 'How can I contact you?', 'quick_question_3' => '', 'quick_questions' => "What services do you offer?\nHow can I contact you?", 'additional_ai_context' => '', 'bubble_color' => '#5b6df8', 'bubble_opacity' => 0, 'bubble_size' => 64, 'bubble_icon' => 'chat', 'bubble_icon_custom_url' => '', 'header_color' => '#5b6df8', 'header_opacity' => 0, 'send_button_color' => '#5b6df8', 'chat_background_color' => '#ffffff', 'chat_background_opacity' => 0, 'logo_url' => '', 'avatar_preset' => 'ai-robot', 'avatar_custom_url' => '', 'avatar_url' => CHATNEX_PLUGIN_URL . 'assets/avatars/ai-robot.svg', 'ai_emoji_enabled' => 1, 'ai_emoji_level' => 50, 'ai_tone_style' => 'friendly', 'ai_warmth_level' => 70, 'ai_contact_stage_action' => 'no_contact_flow', 'ai_contact_strategy_balance' => 50, 'ai_cta_level' => 50, 'ai_uncertainty_level' => 80, 'ai_contact_offer_mode' => 'never', 'ai_registration_offer_mode' => 'never', 'ai_contact_trigger_after_ai_replies' => 0 ]; $saved = get_option($this->option_name, []); $settings = wp_parse_args($saved, $defaults); if (empty($settings['ai_contact_stage_action'])) { $settings['ai_contact_stage_action'] = 'no_contact_flow'; } if (!in_array($settings['ai_contact_stage_action'], ['no_contact_flow', 'request_customer_contact', 'show_company_contacts', 'mixed_contact_flow'], true)) { $settings['ai_contact_stage_action'] = 'no_contact_flow'; } if (empty($settings['quick_question_1']) || empty($settings['quick_question_2'])) { $items = array_values(array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', (string) $settings['quick_questions'])))); if (!empty($items[0]) && empty($settings['quick_question_1'])) { $settings['quick_question_1'] = $items[0]; } if (!empty($items[1]) && empty($settings['quick_question_2'])) { $settings['quick_question_2'] = $items[1]; } if (!empty($items[2]) && empty($settings['quick_question_3'])) { $settings['quick_question_3'] = $items[2]; } } $settings['avatar_url'] = $this->resolve_avatar_url($settings); return $settings; } public function render_enabled_field() { $settings = $this->get_settings(); echo ''; } public function render_project_id_field() { $settings = $this->get_settings(); echo ''; } public function render_public_key_field() { $settings = $this->get_settings(); echo ''; echo '' . esc_html(chatnex_admin_text('Viešas projekto raktas, skirtas įskiepio autorizacijai. Tai nėra OpenAI API key. Testavimo etape šį lauką galima palikti tuščią, jei backend jo dar netikrina.', 'Public project key used to authorize the plugin. This is not an OpenAI API key. During testing, this field may be left empty if the backend does not validate it yet.')) . '
'; } public function render_lead_recipients_field() { $settings = $this->get_settings(); $recipients = isset($settings['lead_recipients']) && is_array($settings['lead_recipients']) ? $settings['lead_recipients'] : []; if (empty($recipients)) { $recipients = ['']; } echo '' . esc_html(chatnex_admin_text('Kiekvienas gavėjas įvedamas atskiroje eilutėje.', 'Enter each recipient on a separate line.')) . '
'; echo 'get_settings(); $value = isset($settings['widget_language']) ? (string) $settings['widget_language'] : 'en'; echo ''; echo '
' . esc_html(chatnex_admin_text('Keičia ChatNEX sąsajos tekstus: būseną, laukelį, siuntimo mygtuką ir sistemos pranešimus.', 'Changes visitor-facing ChatNEX interface text: status, input field, send button and system messages.')) . '
'; } public function render_chat_title_field() { $settings = $this->get_settings(); echo ''; } public function render_helper_text_field() { $settings = $this->get_settings(); echo ''; } public function render_welcome_text_field() { $settings = $this->get_settings(); echo ''; } public function render_quick_questions_field() { $settings = $this->get_settings(); $q1 = $settings['quick_question_1'] ?? ''; $q2 = $settings['quick_question_2'] ?? ''; $q3 = $settings['quick_question_3'] ?? ''; echo '' . esc_html(chatnex_admin_text('Galite nustatyti iki trijų greitų klausimų.', 'You can configure up to three quick questions.')) . '
'; echo '' . esc_html(chatnex_admin_text('Pavyzdžiai:', 'Examples:')) . '
';
echo esc_html(chatnex_admin_text('Standartinį puslapį paruošiame per 4–5 darbo dienas.', 'A standard website is completed within 4–5 business days.')) . '
';
echo esc_html(chatnex_admin_text('Verslo puslapį ir el. parduotuvę paruošiame iki 3 savaičių.', 'A business website or online store is completed within 3 weeks.')) . '
';
echo esc_html(chatnex_admin_text('Jei klientas klausia dėl individualaus 3D gaminio, paraginti susisiekti arba registruotis.', 'When a customer asks about a custom 3D product, encourage them to contact or register.')) . '
';
echo esc_html(chatnex_admin_text('Perkant 10–24 vnt. taikoma 10 % nuolaida.', 'A 10% discount applies to orders of 10–24 units.')) . '
' . esc_html(chatnex_admin_text('Čia rašoma papildoma oficiali informacija AI atsakymams, kurios nėra puslapio turinyje. Šis tekstas siunčiamas į centrinį ChatNEX backend ir gali būti naudojamas atsakant lankytojams.', 'Enter official information that is not available on the website. It is sent to the central ChatNEX backend and may be used when answering visitors.')) . '
'; } private function hex_to_rgba($hex, $transparency_percent = 0) { $hex = ltrim((string) $hex, '#'); if (strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } if (strlen($hex) !== 6) { return 'rgba(91,109,248,1)'; } $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); $transparency = max(0, min(100, ((int) $transparency_percent))); $a = 1 - ($transparency / 100); return sprintf('rgba(%d,%d,%d,%.3f)', $r, $g, $b, $a); } private function is_frontend_html_request() { if (is_admin()) { return false; } if (function_exists('wp_doing_ajax') && wp_doing_ajax()) { return false; } if (function_exists('wp_is_json_request') && wp_is_json_request()) { return false; } if (is_feed() || is_robots() || is_trackback()) { return false; } return true; } private function get_public_payload($settings) { return [ 'enabled' => (bool) $settings['enabled'], 'backendUrl' => 'https://chat.enex.lt', 'projectId' => $settings['project_id'], 'publicKey' => $settings['public_key'], 'leadRecipients' => array_values(array_filter(array_map('strval', (array) ($settings['lead_recipients'] ?? [])))), 'chatTitle' => $settings['chat_title'], 'helperText' => $settings['helper_text'], 'welcomeText' => $settings['welcome_text'], 'quickQuestions' => array_values(array_filter(array_map('trim', preg_split('/\r\n|\r|\n/', $settings['quick_questions'])))), 'additionalAiContext' => (string) ($settings['additional_ai_context'] ?? ''), 'design' => [ 'bubbleBg' => $this->hex_to_rgba($settings['bubble_color'], $settings['bubble_opacity']), 'bubbleSize' => max(44, min(96, (int) ($settings['bubble_size'] ?? 64))), 'bubbleIcon' => $settings['bubble_icon'], 'bubbleIconCustomUrl' => $settings['bubble_icon_custom_url'], 'headerBg' => $this->hex_to_rgba($settings['header_color'], $settings['header_opacity']), 'sendBg' => $this->hex_to_rgba($settings['send_button_color'], 0), 'chatBg' => $this->hex_to_rgba($settings['chat_background_color'], $settings['chat_background_opacity']), 'logoUrl' => $settings['logo_url'], 'avatarUrl' => $settings['avatar_url'], ], 'aiBehavior' => [ 'emoji_enabled' => !empty($settings['ai_emoji_enabled']), 'emoji_level' => (int) $settings['ai_emoji_level'], 'tone_style' => (string) $settings['ai_tone_style'], 'warmth_level' => (int) $settings['ai_warmth_level'], 'contact_stage_action' => (string) ($settings['ai_contact_stage_action'] ?? 'no_contact_flow'), 'contact_strategy_balance' => (int) ($settings['ai_contact_strategy_balance'] ?? $settings['ai_cta_level'] ?? 50), 'uncertainty_level' => (int) $settings['ai_uncertainty_level'], 'contact_offer_mode' => 'never', 'registration_offer_mode' => 'never', 'contact_trigger_after_ai_replies' => (int) ($settings['ai_contact_trigger_after_ai_replies'] ?? 0), ], 'siteUrl' => home_url('/'), 'widgetLanguage' => (string) ($settings['widget_language'] ?? 'lt'), 'pluginVersion' => CHATNEX_VERSION, 'pageUrl' => (is_singular() ? get_permalink() : home_url(add_query_arg([], $GLOBALS['wp']->request ?? ''))), 'strings' => (($settings['widget_language'] ?? 'lt') === 'en') ? [ 'inputPlaceholder' => 'Type your message...', 'send' => 'Send', 'online' => 'Online', 'demoMode' => 'Demo mode', 'helper' => 'Our AI assistant is here to help 🙂', 'hideTarget' => 'Drag here to hide', 'connecting' => 'Checking information...', 'checking' => 'Checking information...', 'error' => 'Could not get an answer. Please try again.', 'timeout' => 'The answer took too long. Please try again.', 'limitAlmostReached' => 'Message limit is almost reached: 1 message left.', 'rateLimitGeneric' => 'Message limit reached. Please try again later 🙂', 'rateLimitDaily' => 'Daily message limit reached. Please try again tomorrow 🙂', 'rateLimitMinute' => 'Too many requests. Please try again in a minute 🙂', 'rateLimitBurst' => 'Messages are being sent too quickly. Please wait a moment 🙂', 'rateLimitSession' => 'Conversation message limit reached. Please try again later 🙂', 'rateLimitDuplicate' => 'The same message is repeated too often. Please rephrase it 🙂', 'demoReply' => 'ChatNEX is connected. The backend is not configured yet, so a demo reply is shown 🙂' ] : [ 'inputPlaceholder' => 'Parašykite žinutę...', 'send' => 'Siųsti', 'online' => 'Prisijungęs', 'demoMode' => 'Demo režimas', 'helper' => 'Mūsų Dirbtinis intelektas Jums padės 🙂', 'hideTarget' => 'Nutempkite čia, kad paslėptumėte', 'connecting' => 'Tikrinama informacija...', 'checking' => 'Tikrinama informacija...', 'error' => 'Nepavyko gauti atsakymo. Bandykite dar kartą.', 'timeout' => 'Atsakymas užtruko per ilgai. Bandykite dar kartą.', 'limitAlmostReached' => 'Žinučių limitas beveik pasiektas, liko 1 žinutė!', 'rateLimitGeneric' => 'Pasiektas pranešimų limitas. Pabandykite vėliau 🙂', 'rateLimitDaily' => 'Pasiekėte dienos pranešimų limitą. Bandykite rytoj 🙂', 'rateLimitMinute' => 'Per daug užklausų per trumpą laiką. Pabandykite po minutės 🙂', 'rateLimitBurst' => 'Per greitai siunčiamos žinutės. Palaukite kelias sekundes 🙂', 'rateLimitSession' => 'Pasiekėte šio pokalbio pranešimų limitą. Pabandykite vėliau 🙂', 'rateLimitDuplicate' => 'Ta pati žinutė kartojama per dažnai. Pabandykite performuluoti 🙂', 'demoReply' => 'ChatNEX prijungtas. Backend dar nesukonfigūruotas, todėl rodomas demo atsakymas 🙂' ] ]; } private function get_inline_bootstrap_markup($settings) { $json_flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT; $payload = wp_json_encode($this->get_public_payload($settings), $json_flags); $css_url = wp_json_encode(esc_url_raw(CHATNEX_PLUGIN_URL . 'public/chat.css?ver=' . CHATNEX_VERSION), $json_flags); $js_url = wp_json_encode(esc_url_raw(CHATNEX_PLUGIN_URL . 'public/chat.js?ver=' . CHATNEX_VERSION), $json_flags); $script = '(function(){ var data = ' . $payload . '; window.ChatNEXData = data; if (!document.getElementById("chatnex-root")) { var root = document.createElement("div"); root.id = "chatnex-root"; root.setAttribute("aria-live", "polite"); (document.body || document.documentElement).appendChild(root); } if (!document.getElementById("chatnex-public-css")) { var css = document.createElement("link"); css.id = "chatnex-public-css"; css.rel = "stylesheet"; css.href = ' . $css_url . '; (document.head || document.documentElement).appendChild(css); } if (!document.getElementById("chatnex-public-js")) { var js = document.createElement("script"); js.id = "chatnex-public-js"; js.src = ' . $js_url . '; js.defer = true; (document.body || document.documentElement).appendChild(js); } })();'; return '' . wp_get_inline_script_tag($script, ['id' => 'chatnex-inline-loader']); } public function inject_chatnex_markup_into_html($html) { if (!chatnex_service_connected()) { return $html; } $settings = $this->get_settings(); if (empty($settings['enabled']) || !is_string($html) || trim($html) === '') { return $html; } $content_type = ''; foreach (headers_list() as $header) { if (stripos($header, 'Content-Type:') === 0) { $content_type = strtolower(trim(substr($header, strlen('Content-Type:')))); break; } } if ($content_type && strpos($content_type, 'text/html') === false) { return $html; } if (stripos($html, 'get_inline_bootstrap_markup($settings); if (stripos($html, '') !== false) { return preg_replace('/<\/body>/i', $markup . '